元类的"__call__"和实例的"__init__"之间的关系是什么?

7

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

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,)

问题:

为什么Meta.__call__只是被触发,但是ProductClass.__init__却没有被触发?

更新:

现在我为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__函数?


你的 Meta.__call__() 没有返回任何内容。它需要返回作为第一个参数 cls 传递的类的实例。通常可以通过调用其父(也称为基)类中同名方法来实现。这可以通过硬编码来完成,即 return type.__call__(, *args),或者使用 return super(Meta, cls).__call__(*args) - martineau
2个回答

8

OOP中扩展方法和重写方法有所不同,在您的元类Meta中所做的就是重写,因为您定义了__call__方法并且没有调用父类的__call__方法。要获得所需的行为,您需要通过调用父方法来扩展__call__方法:

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

4

是的 - 由Meta.__call__决定是否调用ProductClass.__init__(或不调用)。

引用文档:

例如,在元类中定义自定义__call__()方法允许在调用类时进行自定义行为,例如不总是创建新实例。

该页面还提到了一种情况,即元类的__call__可能返回不同类的实例(即在您的示例中不是ProductClass)。 在这种情况下,自动调用ProductClass.__init__显然是不适当的。


如果在ProductClass中有一个“__new__”,那么Meta的“__call__”会调用ProductClass的“__new__”和“__init__”吗?请参见我的更新。 - Alcott
显然,在调用ProductClass的“__new__”之前,Meta的“__call__”被首先调用。 - Alcott
问题在于需要调用ProductClass.__new__ProductClass.__init__,而这需要通过Meta.__call__来实现。通常情况下,type.__call__会为您执行此操作,但是当您定义了Meta.__call__时,您将覆盖该行为,这意味着除非您这样做,否则不会执行它。因此,您要么需要自己调用__new____init__,要么调用类似于type.__call__(cls, *args)的内容。 - Brendan Smithyman

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