我有一个从其父级继承装饰属性的类。我想再添加一个装饰器(@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)
答案 0 :(得分:2)
没有办法做到这一点,因为装饰者不会“知道”在它之前或之后应用的任何其他装饰器。装饰器不是附加到函数的额外信息;相反,当你使用装饰器时,用装饰版本替换原始函数。这不像你有一个单独装饰器列表的功能;你只是得到了应用所有装饰器的最终结果,压缩成一个对象,你以后不能偷看内部并知道哪些装饰器有效。所以当你写这个:
class SomeClass:
"""That's te base class"""
_some_property = None
@property
@thedecorator2
def someproperty(self):
return self._some_property
现在SomeClass.someproperty
是一个属性对象。该属性对象包含thedecorator2
,但property
对thedecorator2
一无所知;它只是将对象包裹起来。您定义了一个名为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
的赋值调用两者。