
在看《 PYTHON 学习手册》这本书关于装饰器的内容时,遇到了一个费解的问题,涉及两个装饰器。
一、第一个装饰器
class Tracer:
def __init__(self, aClass): print("init") self.aClass = aClass def __call__(self, *args): self.wrapped = self.aClass(*args) #语句 1 return self def __getattr__(self, attrname): print('Trace: ' + attrname) return getattr(self.wrapped, attrname) @Tracer
class Person:
def __init__(self, name): self.name = name bob = Person('Bob')
print(bob.name)
sue = Person('Sue')
print(sue.name)
print(bob.name) #bob.name 被 sue.name 覆盖
上面代码输出如下:
init
Trace: name
Bob
Trace: name
Sue
Trace: name
Sue
可见 bobname 被后创建的 sue.name 覆盖了。书上对此现象的解释是:该装饰器对于一个给定的类的多个实例并不是很有效,每个实例构建调用会触发__call__,这会覆盖前面的实例。直接效果是 Tracer 只保存了一个实例,即最后创建的一个实例。但是这个说法对于下面的第二个装饰器好像就不适用了。
二、第二个装饰器
def Private(*privates):
def onDecorator(aClass): class onInstance: def __init__(self, *args, **kargs): print("init") self.wrapped = aClass(*args, **kargs) #语句 2 def __getattr__(self, attr): if attr in privates: raise TypeError('private attribute fetch: ' + attr) else: return getattr(self.wrapped, attr) def __setattr__(self, attr, value): if attr == 'wrapped': self.__dict__[attr] = value elif attr in privates: raise TypeError('private attribute change: ' + attr) else: setattr(self.wrapped, attr, value) return onInstance return onDecorator if name == 'main':
@Private('data', 'size') class Doubler: def __init__(self, label, start): self.label = label self.data = start X = Doubler('X is', [1, 2, 3]) Y = Doubler('Y is', [-10, -20, -30]) print("X.label1=",X.label) print('Y.label=',Y.label) print("X.label2=",X.label) 上面的代码输出如下:
init
init
X.label1= X is
Y.label= Y is
X.label2= X is
我的问题如下:
1、同样是将类实例化两次,为何第一个装饰器中__init__只执行一次,但是第二个装饰器却执行了两次__init__?
2、第二个装饰器中,虽然 Y 是在 X 后面才创建的,但是 X.label 并没有被 Y.label 覆盖! 我觉得第一个装饰器的语句 1 和第二个装饰器的语句 2 应该起到的是同样的效果, 为何第二个装饰器中没有发生新实例覆盖旧实例的现象呢?
恳请大家指点,万分感谢!
1 chenstack 2017 年 8 月 22 日 1. 这是不带参数的类装饰器,Person 对应 aClass 这个形参。之后使用的 Person 实际是 Tracer 的一个实例。而两次 Person(),其实是在这个实例上调用__call__。所以只有一次__init__,wrapped 会覆盖。 2. Private 装饰器返回的是函数内声明 onInstance 类,每次 Doubler()会新创建实例,所以有多个__init__调用,故没有覆盖。 |