使用元类理解 __call__

10

据我理解,类中的__call__方法实现了函数调用运算符,例如:

class Foo:
    def __init__(self):
        print("I'm inside the __init__ method")

    def __call__(self):
        print("I'm inside the __call__ method")

x = Foo() #outputs "I'm inside the __init__ method"
x() #outputs "I'm inside the __call__ method"

然而,我正在阅读Python Cookbook,作者定义了一个元类来控制实例的创建,以便您不能直接实例化一个对象。他是这样做的:

class NoInstance(type):
    def __call__(self, *args, **kwargs):
        raise TypeError("Can't instantaite class directly")


class Spam(metaclass=NoInstance):
    @staticmethod
    def grok(x):
        print("Spam.grok")

Spam.grok(42) #outputs "Spam.grok"

s = Spam() #outputs TypeError: Can't instantaite class directly

然而,我不明白的是为什么没有调用s(),但它的__call__方法却被调用了。这是如何工作的?
2个回答

10
元类实现类的行为(而不是实例)。因此,当您查看实例创建时:
x = Foo()

这个字面上 "调用" 类 Foo。这就是为什么元类的 __call__ 在你的类的 __new____init__ 方法初始化实例之前被调用。

正如 @Take_Care_ 在评论中指出的那样,关于元类的一个很好的资源是 ionelmc的博客文章,名为“理解Python元类”。该博客文章中的一张图片直接适用于您的情况:

enter image description here

该图片直接从博客文章复制而来。


1
很棒而且简单的答案!如果OP想要更多内容:https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/ - Take_Care_
1
谢谢。这正是我在寻找的! - electro7912
这让它看起来像是魔法一样。还要阅读Ignacio在下面的简单答案,并且再次阅读,直到这个答案(和包含的图表)不再显得神奇为止。(在你这样做之后,你会意识到type.__call__的实现实际上是神奇的 - 它根据第一个参数/调用者的不同而表现出不同的行为!) - BadZen

3
一个类只是其元类的实例。由于元类定义了__call__(),因此调用元类的实例,即类,作为函数,即构造函数,将会调用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接