为什么在元类的 __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 不是可迭代的。

所以这里的修复只是删除额外的 clssuper().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__.