San*_*raj 2 python decorator python-decorators
首先,为长期解释道歉.
class A(object):
def __init__(self, klass):
print "A::__init__()"
self._klass = klass
def __call__(self):
print "A::__call__()"
return self._klass()
def __del__(self):
print "A::__del__()"
@A
class B(object):
def __init__(self):
print "B::__init__()"
def main():
b = B()
if __name__ == "__main__":
main()
Run Code Online (Sandbox Code Playgroud)
A::__init__()
A::__call__()
B::__init__()
A::__del__()
Run Code Online (Sandbox Code Playgroud)
class A(object):
def __init__(self, klass):
print "A::__init__()"
self._klass = klass
def __call__(self):
print "A::__call__()"
return self._klass()
def __del__(self):
print "A::__del__()"
class Parent1(object):
def __init__(self):
print "Parent1:: __init__()"
super(Parent1, self).__init__()
class Parent2(object):
def __init__(self):
print "Parent2:: __init__()"
super(Parent2, self).__init__()
@A
class B(Parent1, Parent2):
def __init__(self):
print "B::__init__()"
# super(B, self).__init__()
Parent1.__init__(self)
Parent2.__init__(self)
def main():
b = B()
if __name__ == "__main__":
main()
Run Code Online (Sandbox Code Playgroud)
A::__init__()
A::__call__()
B::__init__()
Parent1:: __init__()
Parent2:: __init__()
Parent2:: __init__()
A::__del__()
Run Code Online (Sandbox Code Playgroud)
super()class A(object):
def __init__(self, klass):
print "A::__init__()"
self._klass = klass
def __call__(self):
print "A::__call__()"
return self._klass()
def __del__(self):
print "A::__del__()"
class Parent1(object):
def __init__(self):
print "Parent1:: __init__()"
super(Parent1, self).__init__()
class Parent2(object):
def __init__(self):
print "Parent2:: __init__()"
super(Parent2, self).__init__()
@A
class B(Parent1, Parent2):
def __init__(self):
print "B::__init__()"
super(B, self).__init__()
def main():
b = B()
if __name__ == "__main__":
main()
Run Code Online (Sandbox Code Playgroud)
A::__init__()
A::__call__()
B::__init__()
Traceback (most recent call last):
File "so.py", line 40, in <module>
main()
File "so.py", line 36, in main
b = B()
File "so.py", line 10, in __call__
return self._klass()
File "so.py", line 32, in __init__
super(B, self).__init__()
TypeError: must be type, not A
A::__del__()
Run Code Online (Sandbox Code Playgroud)
版本#1仅供参考.它解释了我正在尝试做什么,即捕获creation和deletion对象class B.
在版本#2中,我尝试了相同的对象,class B这些对象是从中派生出来的Parent1,Parent2并且使用明确初始化,Parent1.__init__(self)并且Parent2.__init__(self)可以正常工作.
但是在版本#3中,我尝试了同样的super()方法.但我得到以下错误 - TypeError: must be type, not A.我认为这是因为链中__init__()所有父类的方法都MRO没有被正确调用 - 为什么?而且,我该如何解决这个问题?
主要问题是super第一个参数需要是实际的类,但在版本3中,在
super(B, self)
Run Code Online (Sandbox Code Playgroud)
B不是你创建的类.它是A包装类的实例.你需要做类似的事情
class _B(Parent1, Parent2):
def __init__(self):
print "B::__init__()"
super(_B, self).__init__()
B = A(_B)
Run Code Online (Sandbox Code Playgroud)
或者不是B在A实例中包装,而是使用装饰器用包装器替换B's __init__和__del__方法而不替换整个B类.
此外,如果要跟踪B实例的删除,则__del__方法A将不会执行此操作.它将跟踪类的删除,而不是单个实例.
这是一个装饰器应该做你想要的,没有很多问题来自将类包装在非类的东西中:
def track_creation_and_deletion(klass):
original_init = klass.__init__
try:
original_del = klass.__del__
except AttributeError:
def original_del(self):
pass
def new_init(self, *args, **kwargs):
print '{}.{}.__init__'.format(klass.__module__, klass.__name__)
return original_init(self, *args, **kwargs)
def new_del(self):
print '{}.{}.__del__'.format(klass.__module__, klass.__name__)
return original_del(self)
# functools.wraps doesn't play nicely with built-in methods,
# so we handle it ourselves
new_init.__name__ = '__init__'
new_init.__doc__ = original_init.__doc__
new_init.__module__ = klass.__module__
new_init.__dict__.update(getattr(original_init, '__dict__', {}))
new_del.__name__ = '__del__'
new_del.__doc__ = original_del.__doc__
new_del.__module__ = klass.__module__
new_del.__dict__.update(getattr(original_del, '__dict__', {}))
klass.__init__ = new_init
klass.__del__ = new_del
return klass
Run Code Online (Sandbox Code Playgroud)
其中大约一半是错误处理和复制一些元数据,使新方法看起来像是由调用者定义的.关键部分是我们定义新的__init__和__del__方法包装并替换类的旧的.当创建一个装饰类的实例时,__init__我们给它的方法将调用我们选择的日志代码.当一个装饰类的实例被垃圾收集时,__del__我们给它的方法将调用其他日志代码.由于我们没有替换类对象本身,因此在super调用中按名称引用类将引用它们需要引用的类.
这种方法的一个限制是很难在我们中检查实例本身__init__,因为即使在包装__init__返回之后它也可能没有完全构造.例如,如果我们尝试print实例,我们可能会触发子类的__str__方法依赖于尚未准备好的子类属性,从而导致AttributeError.