元类的“__call__”和实例的“__init__”之间的关系?

7 投票
2 回答
2574 浏览
提问于 2025-04-17 02:41

假设我有一个元类和一个使用这个元类的类:

class Meta(type):
    def __call__(cls, *args):
        print "Meta: __call__ with", args

class ProductClass(object):
    __metaclass__ = Meta

    def __init__(self, *args):
        print "ProductClass: __init__ with", args

p = ProductClass(1)

输出如下:

Meta: __call__ with (1,)

问题:

为什么 ProductClass.__init__ 没有被触发...仅仅是因为 Meta.__call__ 的原因吗?

更新:

现在,我为 ProductClass 添加了 __new__ 方法:

class ProductClass(object):
    __metaclass__ = Meta

    def __new__(cls, *args):
        print "ProductClass: __new__ with", args
        return super(ProductClass, cls).__new__(cls, *args)

    def __init__(self, *args):
        print "ProductClass: __init__ with", args

p = ProductClass(1)

那么,是不是 Meta.__call__ 有责任去调用 ProductClass 的 __new____init__ 方法呢?

2 个回答

4

没错,是否调用 ProductClass.__init__ 取决于 Meta.__call__ 的决定(有时候可能不调用)。

引用一下 文档

比如在元类中定义一个自定义的 __call__() 方法,可以让类在被调用时有不同的行为,比如不一定每次都创建一个新的实例。

那篇文档还提到了一种情况,就是元类的 __call__ 可能会返回一个不同类的实例(也就是说,不一定是你例子中的 ProductClass)。在这种情况下,自动调用 ProductClass.__init__ 显然是不合适的。

8

在面向对象编程(OOP)中,扩展一个方法和重写一个方法是有区别的。你在你的元类 Meta 中所做的事情叫做重写,因为你定义了自己的 __call__ 方法,但没有调用父类的 __call__ 方法。如果你想要实现你想要的行为,就需要通过调用父类的方法来扩展 __call__ 方法:

class Meta(type):
    def __call__(cls, *args):
        print "Meta: __call__ with", args
        return super(Meta, cls).__call__(*args)

撰写回答