[英]Why does using super() in a __call__ method on a metaclass raise TypeError?
我正在使用Python 3,我发现我不能在元类的__call__
中使用super()
。
为什么在下面的代码中super()
引发TypeError: 'ListMetaclass' object is not iterable
异常? 如果我从元类中删除__call__
方法,为什么它运行良好?
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
new_cls = type.__new__(cls, name, bases, attrs)
return new_cls
def __call__(cls, *args, **kw):
### why the 'super()' here raises TypeError: 'ListMetaclass' object is not iterable
return super().__call__(cls, *args, **kw)
return super(__class__, cls).__call__(cls, *args, **kw)
return super(__class__, cls.__class__).__call__(cls, *args, **kw)
class MyList(list, metaclass=ListMetaclass):
a=1
def bar(self):
print('test');
L=MyList()
L.add(1)
print('Print MyList :', L)
你不应该将cls
传递给super().__call__()
; super()
负责为你绑定,因此cls
已经自动传入。
你可能会对__new__
的super().__new__(cls, ...)
调用__new__
; 那是因为__new__
是这里的例外,请参阅object.__new__
文档 :
被调用以创建类
cls. __new__()
的新实例cls. __new__()
cls. __new__()
是一个静态方法(特殊情况下,因此您不需要声明它),它将请求实例的类作为其第一个参数。
从super().__call__(...)
表达式中删除cls
有效:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
new_cls = type.__new__(cls, name, bases, attrs)
return new_cls;
def __call__(cls, *args, **kw):
return super().__call__(*args, **kw)
通过传入cls
,您实际上正在执行list(cls)
,告诉list()
将cls
转换为新列表中的值; 这要求cls
是可迭代的。
当你删除元类上的__call__
方法时,当你调用MyClass()
时会使用默认type.__call__
方法, type.__call__
接收普通参数(在你的例子中没有)并返回一个新实例。
我可以看到这会让人感到困惑,因为你必须将cls
传递给super().__new__
但你不能将它传递给super().__call__
。
那是因为__call__
是一种常规方法,但__new__
是特殊的。 它的行为类似于staticmethod
:
object.__new__(cls[, ...])
被调用以创建类
cls
的新实例。__new__()
是一个静态方法(特殊的,所以你不需要声明它)[...]
作为staticmethod,它需要所有参数,而您不需要将第一个参数传递给普通方法(它已经绑定)。
所以只需将其更改为:
return super().__call__(*args, **kw)
在__call__
里面。
使用super().method
访问超类的super().method
已经将方法绑定到当前实例。 因此,方法的self
参数将自动设置,就像调用self.method()
。
因此,当您将当前实例(在本例中为cls
类型)传递给方法时,您实际上是第二次传递它。
super().__call__(cls, *args, **kw)
所以这最终会调用__call__(cls, cls, *args, **kw)
。
当这由__call__
方法解释时,参数将匹配以下定义:
def __call__(cls, *args, **kw):
所以第一个cls
正确匹配,但第二个cls
被解释为变量参数列表*args
。 这里是您的异常来自: cls
正在传递可预期的迭代,但cls
,类ListMetaclass
,是不可迭代的。
所以这里的修复只是删除那些额外的cls
: super().method()
由于方法绑定已经自动包含它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.