为什么在元类的__call__方法中使用super()会引发TypeError?

时间:2017-09-30 12:51:54

标签: python python-3.x super metaclass

我正在使用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)

3 个答案:

答案 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不可迭代。

因此,此处的修复只是删除其他clssuper().method()已经因方法绑定而自动包含它。