了解 __call__ 与元类
Understanding __call__ with metaclasses
根据我的理解,class 中的 __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 并且作者定义了一个 metaclass 来控制实例创建,这样你就不能直接实例化一个对象。他是这样做的:
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__
方法被调用了。这是如何工作的?
A class 只是其 metaclass 的一个实例。由于metaclass定义了__call__()
,调用metaclass的实例,即class,作为一个函数,即作为构造函数,将调用它。
Metaclasses 实现了 class 的行为方式(不是实例)。所以当你查看实例创建时:
x = Foo()
这个字面意思是"calls"classFoo
。这就是为什么在 class 的 __new__
和 __init__
方法初始化实例之前调用 metaclass 的 __call__
的原因。
正如@Take_Care_ 在评论中指出的,metaclasses 上的一项重要资源是 ionelmc's blog post 关于 "Understanding Python metaclasses"。该博客中的一张图片 post 直接适用于您的情况:
图片直接复制自博客post。
根据我的理解,class 中的 __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 并且作者定义了一个 metaclass 来控制实例创建,这样你就不能直接实例化一个对象。他是这样做的:
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__
方法被调用了。这是如何工作的?
A class 只是其 metaclass 的一个实例。由于metaclass定义了__call__()
,调用metaclass的实例,即class,作为一个函数,即作为构造函数,将调用它。
Metaclasses 实现了 class 的行为方式(不是实例)。所以当你查看实例创建时:
x = Foo()
这个字面意思是"calls"classFoo
。这就是为什么在 class 的 __new__
和 __init__
方法初始化实例之前调用 metaclass 的 __call__
的原因。
正如@Take_Care_ 在评论中指出的,metaclasses 上的一项重要资源是 ionelmc's blog post 关于 "Understanding Python metaclasses"。该博客中的一张图片 post 直接适用于您的情况:
图片直接复制自博客post。