我正在使用Python 3,我发现我无法在元类的super()
中使用__call__
。
为什么在下面的代码中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)
答案 0 :(得分:4)
您不应该将cls
传递给super().__call__()
; super()
会为您处理绑定,因此cls
已经已经自动传入。
您可能对super().__new__(cls, ...)
中的__new__
电话感到困惑;这是因为__new__
是例外,请参阅object.__new__
documentation:
调用创建类
cls. __new__()
的新实例是一个静态方法(特殊的,所以你不需要声明它),它将请求实例的类作为它的第一个参数。
从cls
表达式中删除super().__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):
return super().__call__(*args, **kw)
通过传递cls
,您实际上正在执行list(cls)
,告诉list()
将cls
转换为新列表中的值;需要cls
成为可迭代的。
当您删除元类上的__call__
方法时,在调用type.__call__
时会使用默认的MyClass()
方法,它只接收正常参数(在您的示例中为none)并返回一个新的实例。
答案 1 :(得分:2)
我可以看到这会让人感到困惑,因为您必须将cls
传递给super().__new__
,但不得将其传递给super().__call__
。
这是因为__call__
是一种常规方法,但__new__
很特殊。它是behaving like a staticmethod
:
object.__new__(cls[, ...])
调用以创建类
cls
的新实例。__new__()
是一种静态方法(特殊情况,所以你不需要声明它)[...]
作为staticmethod,需要所有参数,而您不需要将第一个参数传递给普通方法(它已经被绑定)。
所以只需将其更改为:
return super().__call__(*args, **kw)
在__call__
内。
答案 2 :(得分:1)
使用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()
已经因方法绑定而自动包含它。