[英]Why does using super() in a __call__ method on a metaclass raise TypeError?
I'm using Python 3, and I found that I can't use super()
within __call__
of metaclass. 我正在使用Python 3,我发现我不能在元类的__call__
中使用super()
。
Why in the code below does super()
raise a TypeError: 'ListMetaclass' object is not iterable
exception? 为什么在下面的代码中super()
引发TypeError: 'ListMetaclass' object is not iterable
异常? Why does it works well if I remove the __call__
method from the metaclass? 如果我从元类中删除__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)
You should not pass in cls
to super().__call__()
; 你不应该将cls
传递给super().__call__()
; super()
takes care of binding for you and so cls
is already passed in automatically. super()
负责为你绑定,因此cls
已经自动传入。
You may have gotten confused by the super().__new__(cls, ...)
call in __new__
; 你可能会对__new__
的super().__new__(cls, ...)
调用__new__
; that's because __new__
is the exception here, see the object.__new__
documentation : 那是因为__new__
是这里的例外,请参阅object.__new__
文档 :
Called to create a new instance of class
cls. __new__()
被调用以创建类cls. __new__()
的新实例cls. __new__()
cls. __new__()
is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument.cls. __new__()
是一个静态方法(特殊情况下,因此您不需要声明它),它将请求实例的类作为其第一个参数。
Removing cls
from the super().__call__(...)
expression works: 从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)
By passing in cls
, you are effectively executing list(cls)
, telling list()
to convert cls
to values in the new list; 通过传入cls
,您实际上正在执行list(cls)
,告诉list()
将cls
转换为新列表中的值; that required cls
to be an iterable. 这要求cls
是可迭代的。
When you remove the __call__
method on your metaclass, the default type.__call__
method is used when you call MyClass()
, which just receives the normal arguments (none in your example) and returns a new instance. 当你删除元类上的__call__
方法时,当你调用MyClass()
时会使用默认type.__call__
方法, type.__call__
接收普通参数(在你的例子中没有)并返回一个新实例。
I can see where this will be confusing because you have to pass cls
to super().__new__
but you mustn't pass it to super().__call__
. 我可以看到这会让人感到困惑,因为你必须将cls
传递给super().__new__
但你不能将它传递给super().__call__
。
That's because __call__
is a normal method but __new__
is special. 那是因为__call__
是一种常规方法,但__new__
是特殊的。 It's behaving like a staticmethod
: 它的行为类似于staticmethod
:
object.__new__(cls[, ...])
Called to create a new instance of class
cls
. 被调用以创建类cls
的新实例。__new__()
is a static method (special-cased so you need not declare it as such) [...]__new__()
是一个静态方法(特殊的,所以你不需要声明它)[...]
As staticmethod it needs all arguments while you don't need to pass the first argument to normal methods (it's already bound). 作为staticmethod,它需要所有参数,而您不需要将第一个参数传递给普通方法(它已经绑定)。
So just change it to: 所以只需将其更改为:
return super().__call__(*args, **kw)
inside __call__
. 在__call__
里面。
Accessing a method of the super class using super().method
will already bind the method to the current instance. 使用super().method
访问超类的super().method
已经将方法绑定到当前实例。 So the self
argument for methods will already be set automatically, just like when you call self.method()
. 因此,方法的self
参数将自动设置,就像调用self.method()
。
So when you then pass the current instance (in this case, the cls
type) to the method, you are actually passing it a second time. 因此,当您将当前实例(在本例中为cls
类型)传递给方法时,您实际上是第二次传递它。
super().__call__(cls, *args, **kw)
So this will end up making a call __call__(cls, cls, *args, **kw)
. 所以这最终会调用__call__(cls, cls, *args, **kw)
。
When this is then interpreted by your __call__
method, the arguments will then be matched to the following definition: 当这由__call__
方法解释时,参数将匹配以下定义:
def __call__(cls, *args, **kw):
So the first cls
is properly matched, but the second cls
is interpreted to be the variable parameter list *args
. 所以第一个cls
正确匹配,但第二个cls
被解释为变量参数列表*args
。 And here's where your exception comes from: cls
is being passed where an iterable is expected, but cls
, the class ListMetaclass
, is not iterable. 这里是您的异常来自: cls
正在传递可预期的迭代,但cls
,类ListMetaclass
,是不可迭代的。
So the fix here is simply to remove that additional cls
: super().method()
will already include it automatically due to method binding. 所以这里的修复只是删除那些额外的cls
: super().method()
由于方法绑定已经自动包含它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.