gpt4 book ai didi

python - 将 super 方法与派生类的类装饰器一起使用时出现 TypeError

转载 作者:行者123 更新时间:2023-11-28 21:21:18 24 4
gpt4 key购买 nike

首先,为冗长的解释道歉。

版本 #1 - 代码:类的类装饰器

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()

版本 #1 - 输出:

A::__init__()
A::__call__()
B::__init__()
A::__del__()

版本 #2 - 代码:派生类的类装饰器,它显式初始化基类。

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()

版本 #2 - 输出:

A::__init__()
A::__call__()
B::__init__()
Parent1:: __init__()
Parent2:: __init__()
Parent2:: __init__()
A::__del__()

版本 #3 - 代码:使用 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()

版本 #3 - 输出:

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__()

问题:

版本 #1 仅供引用。它解释了我正在尝试做的事情,即捕获 class B 对象的 creationdeletion

在版本 #2 中,我对派生自 Parent1Parent2class B 的对象进行了相同的尝试显式使用 Parent1.__init__(self)Parent2.__init__(self) 可以正常工作。

但是在第 3 版中,我尝试使用 super() 方法进行同样的操作。但我收到以下错误 - TypeError: must be type, not A。我认为这是因为未正确调用 MRO 链中所有父类的 __init__() 方法 - 为什么?而且,我该如何解决这个问题?

最佳答案

主要问题是 super 的第一个参数需要是实际的类,但是在版本 3 中,在

super(B, self)

B 不是您创建的类。它是包装类的 A 实例。你需要做类似的事情

class _B(Parent1, Parent2):
def __init__(self):
print "B::__init__()"
super(_B, self).__init__()
B = A(_B)

或者不是将 B 包装在 A 实例中,而是使用装饰器来替换 B__init____del__ 方法与包装器,而不替换整个 B 类。

此外,如果您想跟踪 B 实例的删除,A 上的 __del__ 方法将无法做到这一点。它将跟踪类的删除,而不是单个实例。


这是一个装饰器,它应该做你想做的事,而不会因为将类包装在不是类的东西中而产生许多问题:

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

其中大约一半是错误处理和复制一些元数据以使新方法看起来像是由调用者定义的。关键部分是我们定义了新的 __init____del__ 方法来包装和替换类的旧方法。创建装饰类的实例时,我们为其提供的 __init__ 方法将调用我们选择的日志记录代码。当装饰类的实例被垃圾回收时,我们为其提供的 __del__ 方法将调用其他日志记录代码。由于我们没有替换类对象本身,因此在 super 调用中按名称引用类将引用它们需要引用的类。

这种方法的一个限制是很难在我们的 __init__ 中检查实例本身,因为它可能没有完全构建,即使在包装的 __init__ 返回之后也是如此。例如,如果我们尝试 print 实例,我们可能会触发子类的 __str__ 方法依赖于尚未准备好的子类属性,从而导致 AttributeError。

关于python - 将 super 方法与派生类的类装饰器一起使用时出现 TypeError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21826854/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com