为什么在元类的 __call__ 方法中使用 super() 会引发 TypeError?
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__
是这里的例外,请参阅 object.__new__
documentation:
Called to create a new instance of class 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.
从 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__
方法,它只接收普通参数(none在你的例子中)和 returns 一个新实例。
使用super().method
访问超级class的方法将已经绑定该方法到当前实例。因此,方法的 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
、class ListMetaclass
不是可迭代的。
所以这里的修复只是删除额外的 cls
:super().method()
由于方法绑定已经自动包含它。
我知道这会让人困惑,因为你必须将 cls
传递给 super().__new__
但你 不能 将它传递给 super().__call__
.
那是因为__call__
是一个普通的方法,而__new__
是一个特殊的方法。这是 behaving like a staticmethod
:
object.__new__(cls[, ...])
Called to create a new instance of class cls
. __new__()
is a static method (special-cased so you need not declare it as such) [...]
作为静态方法,它需要所有参数,而您不需要将第一个参数传递给普通方法(它已经绑定)。
所以只需将其更改为:
return super().__call__(*args, **kw)
里面 __call__
.
我正在使用 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__
是这里的例外,请参阅 object.__new__
documentation:
Called to create a new instance of class
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.
从 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__
方法,它只接收普通参数(none在你的例子中)和 returns 一个新实例。
使用super().method
访问超级class的方法将已经绑定该方法到当前实例。因此,方法的 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
、class ListMetaclass
不是可迭代的。
所以这里的修复只是删除额外的 cls
:super().method()
由于方法绑定已经自动包含它。
我知道这会让人困惑,因为你必须将 cls
传递给 super().__new__
但你 不能 将它传递给 super().__call__
.
那是因为__call__
是一个普通的方法,而__new__
是一个特殊的方法。这是 behaving like a staticmethod
:
object.__new__(cls[, ...])
Called to create a new instance of class
cls
.__new__()
is a static method (special-cased so you need not declare it as such) [...]
作为静态方法,它需要所有参数,而您不需要将第一个参数传递给普通方法(它已经绑定)。
所以只需将其更改为:
return super().__call__(*args, **kw)
里面 __call__
.