为啥元类的 __call__ 方法在类上调用,而原生类的 __call__ 没有?

Posted

技术标签:

【中文标题】为啥元类的 __call__ 方法在类上调用,而原生类的 __call__ 没有?【英文标题】:Why metaclass's __call__ method called on class, but native class's __call__ not?为什么元类的 __call__ 方法在类上调用,而原生类的 __call__ 没有? 【发布时间】:2015-08-31 20:44:26 【问题描述】:
class Meta(type):
    def __call__(cls, *args, **kwargs):
        print 'Meta.__call__ called.'
        return super(Meta, cls).__call__(*args, **kwargs)


class MetaInstance(object):
    __metaclass__ = Meta

# Instantiate class.
MetaInstance()  # Meta.__call__ called.


class StandardClass(object):
    @classmethod
    def __call__(cls, *args, **kwargs):
        print 'StandardClass.__call__ called.'
        return super(StandardClass, cls).__call__(*args, **kwargs)

# Instantiate class.
StandardClass()  # StandardClass.__call__ is not called!

当我实例化一个类时,为什么元类的__call__ 方法在类上调用,而本地类的__call__ 却没有?

【问题讨论】:

【参考方案1】:

当你创建一个类实例时,元类的(类是它的实例)__call__ 被调用。

# Instantiate class.
StandardClass()  # StandardClass.__call__ is not called!

当您创建一个类实例然后“调用”该实例时,就会调用该类的__call__。我不认为用classmethod 装饰__call__ 会起作用。

这将调用您的元类和类的__call__s:

StandardClass()()

【讨论】:

我可能错了,但是:第一句话中的“元类的__call__”是否意味着“元类范围内定义的__call__方法和元类的实例(类对象)将有“(someclass.__call__)?那不是更多“类对象的(MetaInstanceStandardClass__call__ 方法”吗?默认元类的__call__ 方法不就是type.__call__ 方法(如果我们滥用语言,也就是type() 函数)? @vmonteco "在元类作用域中定义的__call__ 方法和元类的实例(类对象)将具有" __call__ --它不是由类从其元类继承的。 我不明白你提到的其他事情 然而,如果我使用打印"bar"__call__ 方法定义一个自定义元类类,如果我用这个元类构建我的类foo,并调用foo.__call__"bar"将显示出来。 我认为“在元类的范围和元类的实例(类对象)中定义的 __call__ 方法”在技术上是不正确的,因为“元类的实例(类对象)”不没有这个方法。如果你覆盖它——它不会被调用。魔术方法总是called on the class, not instance。【参考方案2】:

因为magic methods are looked up on the class。换句话说,当您在一个类上编写__call__ 方法时,它定义了当您调用该类的 instance 时会发生什么,而不是当您调用该类本身时会发生什么。类是其元类的一个实例,因此在元类上定义 __call__ 定义了调用该类时会发生什么。

您已经可以在示例中看到,在这两种情况下您没有做同样的事情。对于元类示例,您定义了两个东西,一个元类和一个“元实例”(即,一个常规类),您调用了第二个。对于第二个示例,您只定义了一件事(一个类)。为了与第一个示例平行,您需要创建一个 StandardClass 的实例,然后调用它,在这种情况下将调用 StandardClass.__call__

【讨论】:

以上是关于为啥元类的 __call__ 方法在类上调用,而原生类的 __call__ 没有?的主要内容,如果未能解决你的问题,请参考以下文章

自定义元类控制类的实例化行为

在类中调用父类的 __call__ 方法

Python 元类:为啥在类定义期间不调用 __setattr__ 来设置属性?

元类之控制类的调用过程

002 元类的创建及简单使用

什么可以从元类实例化