Python 对派生类使用带类装饰器的超级方法时出现TypeError
首先,为冗长的解释道歉 版本1-代码:类的类装饰器 版本#1-输出: Version#2-代码:派生类的类装饰器,它显式初始化基类。 版本2-输出: 版本3-代码:使用Python 对派生类使用带类装饰器的超级方法时出现TypeError,python,decorator,python-decorators,Python,Decorator,Python Decorators,首先,为冗长的解释道歉 版本1-代码:类的类装饰器 版本#1-输出: Version#2-代码:派生类的类装饰器,它显式初始化基类。 版本2-输出: 版本3-代码:使用super() 版本#3-输出: A::uuu init_uuuu() A::______;呼叫__()) B::uuu init_uuuu() 回溯(最近一次呼叫最后一次): 文件“so.py”,第40行,在 main() 文件“so.py”,第36行,主 b=b() 调用中第10行的文件“so.py”__ 返回自我。_klas
super()
版本#3-输出:
A::uuu init_uuuu()
A::______;呼叫__())
B::uuu init_uuuu()
回溯(最近一次呼叫最后一次):
文件“so.py”,第40行,在
main()
文件“so.py”,第36行,主
b=b()
调用中第10行的文件“so.py”__
返回自我。_klass()
文件“so.py”,第32行,在_init中__
super(B,self)。\uuuuu init\uuuuuuu()
TypeError:必须是类型,而不是
A::_udel_uu()
问题:
第1版仅供参考。它解释了我试图做的事情,即捕获B类
对象的创建
和删除
在版本2中,我对类B的对象尝试了相同的方法,该类对象派生自Parent1
和Parent2
,它们使用Parent1.\uu init(self)
和Parent2.\uu init(self)
显式初始化,工作正常
但是在版本3中,我用
super()
方法尝试了同样的方法。但是我得到了以下错误-TypeError:必须是type,而不是。我认为这是因为MRO
链中所有父类的\uuu init\uuu()方法没有正确调用-为什么?那么,我该如何解决这个问题呢 主要问题是super
的第一个参数必须是实际的类,但在版本3中,在
super(B, self)
B
不是您创建的类。它是包装类的实例。你需要像这样做
class _B(Parent1, Parent2):
def __init__(self):
print "B::__init__()"
super(_B, self).__init__()
B = A(_B)
或者,不要在一个A
实例中包装B
,而是使用一个修饰符,用包装器替换B
的\uuuuu init\uuuuu
和\uu del\uuuu
方法,而不替换整个B
类
另外,如果要跟踪B
实例的删除,则a
上的\uu del\uuu
方法将无法执行此操作。它将跟踪类的删除,而不是单个实例的删除
这里有一个decorator,它应该做您想做的事情,而不会因为用非类的东西包装类而产生很多问题:
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
其中大约一半是错误处理和复制一些元数据,以使新方法看起来像是由调用方定义的。关键部分是我们定义了新的\uuuu init\uuu
和\uu del\uuu
方法来包装和替换类的旧方法。创建修饰类的实例时,我们给它的\uuuu init\uuuu
方法将调用我们选择的日志代码。当装饰类的实例被垃圾收集时,我们给它的\uu del\uu
方法将调用其他日志代码。因为我们没有替换类对象本身,所以在super
调用中按名称引用类将引用它们需要引用的类
这种方法的一个限制是很难在\uuuuu init\uuu
中检查实例本身,因为即使在包装的\uuuu init\uu
返回后,它也可能没有完全构造。例如,如果我们试图打印
实例,我们可能会触发子类的\uu str\uu
方法,该方法依赖于尚未准备好的子类属性,导致AttributeError。我花了一些时间来理解为什么分别使用\uuuu call\uuu
和\uu del\uu
方法很难捕获对象实例化和对象删除。以下是一些有用的参考资料
- python列表mailer中也讨论了相同的主题-请查看此处的线程-
使用\uu del\uuu
方法可以实现这一点,但它们有副作用!例如,@user2357112给出的答案是一个很好的技巧,但当我们进行循环引用时,它不起作用,因为垃圾收集器无法确定循环引用中要首先调用的\uu del\uu
!然而,这可以通过使用弱ref来避免;但它仍然是一个黑客
其中一个建议是创建一个上下文管理器,它可以创建和删除特定类的对象
我有下面的例子,它模拟了这一点。请仔细查看控制器
装饰器
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__()
def Controller(_cls):
class Wrapper(_cls):
def create(self, name):
ret = _cls.create(self, name)
print "Added to Database! :: ", name
# Database add here!
return ret
def remove(self, name):
ret = _cls.remove(self, name)
print "Deleted from Database! :: ", name
# Database delete here!
return ret
return Wrapper
@Controller
class Manager(object):
def __init__(self):
#print "Manager::__init__()"
self._repo = []
def create(self, name):
a = A(name)
print "Object created :: ", name
self._repo.append(a)
def remove(self, name):
for i, item in enumerate(self._repo):
if item._name == name:
del self._repo[i]
print "Object removed :: ", name
def display(self):
for item in self._repo:
print item
class A(Parent1, Parent2):
def __init__(self, name):
#print "A::__init__()"
self._name = name
super(A, self).__init__()
def __repr__(self):
return self._name
def main():
m1 = Manager()
m1.create("apples")
m1.create("oranges")
m1.create("grapes")
#m1.display()
m1.remove("apples")
#m1.display()
if __name__ == "__main__":
main()
执行时,会产生以下结果:
Object created :: apples
Added to Database! :: apples
Object created :: oranges
Added to Database! :: oranges
Object created :: grapes
Added to Database! :: grapes
Object removed :: apples
Deleted from Database! :: apples
这是我能想出的解决问题的最安全的办法。欢迎您的建议 +1我正在考虑将self.\u klass
传递给\uuu init\uuu
调用:self.\u klass)
,然后在super
调用中使用它,而不是B
。但是,这看起来更好。这里已经回答了:是亚历克斯·马泰利写的,你可以相信那个家伙!:)
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__()
super(B, self)
class _B(Parent1, Parent2):
def __init__(self):
print "B::__init__()"
super(_B, self).__init__()
B = A(_B)
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
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__()
def Controller(_cls):
class Wrapper(_cls):
def create(self, name):
ret = _cls.create(self, name)
print "Added to Database! :: ", name
# Database add here!
return ret
def remove(self, name):
ret = _cls.remove(self, name)
print "Deleted from Database! :: ", name
# Database delete here!
return ret
return Wrapper
@Controller
class Manager(object):
def __init__(self):
#print "Manager::__init__()"
self._repo = []
def create(self, name):
a = A(name)
print "Object created :: ", name
self._repo.append(a)
def remove(self, name):
for i, item in enumerate(self._repo):
if item._name == name:
del self._repo[i]
print "Object removed :: ", name
def display(self):
for item in self._repo:
print item
class A(Parent1, Parent2):
def __init__(self, name):
#print "A::__init__()"
self._name = name
super(A, self).__init__()
def __repr__(self):
return self._name
def main():
m1 = Manager()
m1.create("apples")
m1.create("oranges")
m1.create("grapes")
#m1.display()
m1.remove("apples")
#m1.display()
if __name__ == "__main__":
main()
Object created :: apples
Added to Database! :: apples
Object created :: oranges
Added to Database! :: oranges
Object created :: grapes
Added to Database! :: grapes
Object removed :: apples
Deleted from Database! :: apples