简体   繁体   English

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

[英]Why does using super() in a __call__ method on a metaclass raise TypeError?

I'm using Python 3, and I found that I can't use super() within __call__ of metaclass. 我正在使用Python 3,我发现我不能在元类的__call__中使用super()

Why in the code below does super() raise a TypeError: 'ListMetaclass' object is not iterable exception? 为什么在下面的代码中super()引发TypeError: 'ListMetaclass' object is not iterable异常? Why does it works well if I remove the __call__ method from the metaclass? 如果我从元类中删除__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)

You should not pass in cls to super().__call__() ; 你不应该将cls传递给super().__call__() ; super() takes care of binding for you and so cls is already passed in automatically. super()负责为你绑定,因此cls 已经自动传入。

You may have gotten confused by the super().__new__(cls, ...) call in __new__ ; 你可能会对__new__super().__new__(cls, ...)调用__new__ ; that's because __new__ is the exception here, see the object.__new__ documentation : 那是因为__new__是这里的例外,请参阅object.__new__文档

Called to create a new instance of class cls. __new__() 被调用以创建类cls. __new__()的新实例cls. __new__() 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. cls. __new__()是一个静态方法(特殊情况下,因此您不需要声明它),它将请求实例的类作为其第一个参数。

Removing cls from the super().__call__(...) expression works: 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)

By passing in cls , you are effectively executing list(cls) , telling list() to convert cls to values in the new list; 通过传入cls ,您实际上正在执行list(cls) ,告诉list()cls转换为新列表中的值; that required cls to be an iterable. 这要求cls是可迭代的。

When you remove the __call__ method on your metaclass, the default type.__call__ method is used when you call MyClass() , which just receives the normal arguments (none in your example) and returns a new instance. 当你删除元类上的__call__方法时,当你调用MyClass()时会使用默认type.__call__方法, type.__call__接收普通参数(在你的例子中没有)并返回一个新实例。

I can see where this will be confusing because you have to pass cls to super().__new__ but you mustn't pass it to super().__call__ . 我可以看到这会让人感到困惑,因为你必须将cls传递给super().__new__但你不能将它传递给super().__call__

That's because __call__ is a normal method but __new__ is special. 那是因为__call__是一种常规方法,但__new__是特殊的。 It's behaving like a staticmethod : 它的行为类似于staticmethod

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

Called to create a new instance of class cls . 被调用以创建类cls的新实例。 __new__() is a static method (special-cased so you need not declare it as such) [...] __new__()是一个静态方法(特殊的,所以你不需要声明它)[...]

As staticmethod it needs all arguments while you don't need to pass the first argument to normal methods (it's already bound). 作为staticmethod,它需要所有参数,而您不需要将第一个参数传递给普通方法(它已经绑定)。

So just change it to: 所以只需将其更改为:

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

inside __call__ . __call__里面。

Accessing a method of the super class using super().method will already bind the method to the current instance. 使用super().method访问超类的super().method已经方法绑定到当前实例。 So the self argument for methods will already be set automatically, just like when you call self.method() . 因此,方法的self参数将自动设置,就像调用self.method()

So when you then pass the current instance (in this case, the cls type) to the method, you are actually passing it a second time. 因此,当您将当前实例(在本例中为cls类型)传递给方法时,您实际上是第二次传递它。

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

So this will end up making a call __call__(cls, cls, *args, **kw) . 所以这最终会调用__call__(cls, cls, *args, **kw)

When this is then interpreted by your __call__ method, the arguments will then be matched to the following definition: 当这由__call__方法解释时,参数将匹配以下定义:

def __call__(cls, *args, **kw):

So the first cls is properly matched, but the second cls is interpreted to be the variable parameter list *args . 所以第一个cls正确匹配,但第二个cls被解释为变量参数列表*args And here's where your exception comes from: cls is being passed where an iterable is expected, but cls , the class ListMetaclass , is not iterable. 这里是您的异常来自: cls正在传递可预期的迭代,但cls ,类ListMetaclass ,是不可迭代的。

So the fix here is simply to remove that additional cls : super().method() will already include it automatically due to method binding. 所以这里的修复只是删除那些额外的clssuper().method()由于方法绑定已经自动包含它。

暂无
暂无

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

相关问题 使用元类的__call__方法而不是__new__? - Using the __call__ method of a metaclass instead of __new__? 为什么元类的__call__方法调用类,而本机类的__call__不是呢? - Why metaclass's __call__ method called on class, but native class's __call__ not? 为什么带有对象库的元类引发元类冲突? - Why does metaclass with object base raise metaclass conflict? Python:为什么将子类方法的 **kwarg 作为参数传递到基础 class 的 __call__() 签名中? 结果类型错误 - Python: Why would a **kwarg of a subclass method be passed as a parameter in __call__() signature of the base class? Results in TypeError 为什么会这样说:“类型错误:__call__() 缺少 1 个必需的位置参数:‘输入’” - why does it say this: "TypeError: __call__() missing 1 required positional argument: 'inputs'" Django模板:为什么__call__魔术方法破坏了非模型对象的呈现? - Django templates: why does __call__ magic method breaks the rendering of a non-model object? 我如何称呼元类的__call__? - How can I call the metaclass's __call__? __call__ 来自 __init__ 的元类阴影签名 - __call__ from metaclass shadows signature of __init__ 为什么不能动态地向实例添加`__call__` 方法? - Why won't dynamically adding a `__call__` method to an instance work? 元类的“__call__”和实例的“__init__”的关系? - Relationship of metaclass's “__call__” and instance's “__init__”?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM