繁体   English   中英

为什么在元类的__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__ ; 那是因为__new__是这里的例外,请参阅object.__new__文档

被调用以创建类cls. __new__()的新实例cls. __new__() cls. __new__()是一个静态方法(特殊情况下,因此您不需要声明它),它将请求实例的类作为其第一个参数。

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__方法, type.__call__接收普通参数(在你的例子中没有)并返回一个新实例。

我可以看到这会让人感到困惑,因为你必须将cls传递给super().__new__但你不能将它传递给super().__call__

那是因为__call__是一种常规方法,但__new__是特殊的。 它的行为类似于staticmethod

object.__new__(cls[, ...])

被调用以创建类cls的新实例。 __new__()是一个静态方法(特殊的,所以你不需要声明它)[...]

作为staticmethod,它需要所有参数,而您不需要将第一个参数传递给普通方法(它已经绑定)。

所以只需将其更改为:

return super().__call__(*args, **kw)

__call__里面。

使用super().method访问超类的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()由于方法绑定已经自动包含它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM