如何在不复制整个方法的情况下将装饰器添加到Python中的继承方法?

时间:2015-05-01 02:33:30

标签: python inheritance metaprogramming decorator python-decorators

我有一个从其父级继承装饰属性的类。我想再添加一个装饰器(@thedecorator1("yyy")),但不要覆盖整个方法和现有的装饰器。 (装饰器的顺序在某种程度上很重要,decorator1应该总是在property.setter和thedecorator2之间)

在Python 3中可以吗?

from functools import wraps

class thedecorator1:
    def __init__ (self, idx):
        self.idx = idx
    def __call__ (self, func):
        @wraps(func)
        def wrapped(*args, **kwargs):
            print("thedecorator1", self.idx, args)
            return func(*args, **kwargs)
        return wrapped

def thedecorator2(func):
    def _thedecorator2(self, *args, **kwargs):
        print("thedecorator2",args)
        return func(self, *args, **kwargs)
    return _thedecorator2

class SomeClass:
    """That's te base class"""
    _some_property = None

    @property
    @thedecorator2
    def someproperty(self):
        return self._some_property

    @someproperty.setter
    @thedecorator1("xxx")
    @thedecorator2
    def someproperty(self, value):
        self._some_property = value  

class AnotherClass(SomeClass):
    """That's what works, but I need to copy everything everytime"""

    _some_property = None

    @property
    @thedecorator2
    def someproperty(self):
        return self._some_property

    @someproperty.setter
    @thedecorator1("yyy")
    @thedecorator1("xxx")
    @thedecorator2
    def someproperty(self, value):
        self._someproperty = value

class YetAnotherClass(SomeClass):
    """That's what I think I need"""

    dosomethingsmart(target = someproperty, decorator =  thedecorator1("yyy"), after=someproperty.setter)

2 个答案:

答案 0 :(得分:2)

没有办法做到这一点,因为装饰者不会“知道”在它之前或之后应用的任何其他装饰器。装饰器不是附加到函数的额外信息;相反,当你使用装饰器时,用装饰版本替换原始函数。这不像你有一个单独装饰器列表的功能;你只是得到了应用所有装饰器的最终结果,压缩成一个对象,你以后不能偷看内部并知道哪些装饰器有效。所以当你写这个:

class SomeClass:
    """That's te base class"""
    _some_property = None

    @property
    @thedecorator2
    def someproperty(self):
        return self._some_property

现在SomeClass.someproperty是一个属性对象。该属性对象包含thedecorator2,但propertythedecorator2一无所知;它只是将对象包裹起来。您定义了一个名为someproperty的方法,但装饰器将其包装并“隐藏”原始函数;没有通用的方法来“解开”一系列装饰器来访问原始函数或部分装饰的中间函数(例如,应用thedecorator2而不是property的结果)。< / p>

稍后当您定义AnotherClass时,您说您要“添加装饰器”。但到了什么?您唯一可以访问的是SomeClass.someproperty。您定义的原始功能不可用;它被“掩埋”在装饰者之下,你无法将它取回。

现在,对于一些装饰器,您可以将原始功能退出。例如,property将其getter函数存储在其fget属性中。但是装饰器如何(或者如果!)存储原始函数取决于装饰器。例如,您的thedecorator2装饰器不会以任何方式公开func。因此,如果AnotherClass确切地知道最初应用了哪些装饰器,那么<​​em>可能能够使用关于每个装饰器的特定知识来展开它们。但是这仍然需要你知道最初应用了哪些装饰器,所以它不会为你在AnotherClass中不必复制那些信息而节省任何东西。

底线是装饰者不会“保存”他们装饰的原始物品;他们只是将它压成它​​们生产的任何东西。没有通用的方法来“剥离”已装饰的函数的装饰器,甚至不知道应用了哪些装饰器。因此,你不能拥有像insert_new_decorator(somefunction, after=somedecorator)这样的东西,因为没有办法剥离装饰层来知道堆栈中somedecorator的位置。

装饰品不像衣服,你可以脱掉外套,在下面放一件不同的衬衫,然后把原来的夹克重新穿上。相反,想象一下像草莓蘸巧克力,然后蘸奶油,然后淋上糖霜。你不能“打开”奶油和糖霜,并在下面涂上不同的巧克力涂层。整个东西被融合成一个物体,其层不能被拉开。

答案 1 :(得分:0)

考虑这个程序:

from functools import wraps

def dec1(fn):
    @wraps(fn)
    def wrap(*args, **kw):
        print "dec1", fn.__name__
        return fn(*args, **kw)
    return wrap

def dec2(fn):
    @wraps(fn)
    def wrap(*args, **kw):
        print "dec2", fn.__name__
        return fn(*args, **kw)
    return wrap

class C(object):
    prop = None
    @property
    def foo(self):
        return self.prop

    @foo.setter
    @dec1
    def foo(self, x):
        self.prop = x

class D(C):
    foo = property(
        C.foo.fget,
        dec2(C.foo.fset),
        C.foo.fdel)

print '.'
c=C()
c.foo = 7
print '.'
d = D()
d.foo = 8
print '.'

D.foo是一个继承了C.foo的get和delete函数的属性,但包装了set函数。

D的替代定义是:

class D(C):
    foo = C.foo.setter(dec2(C.foo.fset))

在任何一种情况下,程序的输出都是:

.
dec1 foo
.
dec2 foo
dec1 foo
.

请注意,c.foo的赋值调用一个包装器,而d.foo的赋值调用两者。