在啥情况下调用来自父类的多个元类?

Posted

技术标签:

【中文标题】在啥情况下调用来自父类的多个元类?【英文标题】:In which circumstances multiple metaclasses from the parent classes are invoked?在什么情况下调用来自父类的多个元类? 【发布时间】:2015-11-03 23:23:57 【问题描述】:

行为异常的代码(用 Python 2.7.3 测试):

class Meta1(type):
   def __new__(mcl, name, bases, attrs):
        print "Hello Meta1.__new__ "
        return super(Meta1, mcl).__new__(mcl, name, bases, attrs)

class Meta2(type):
    def __new__(mcl, name, bases, attrs):
        print "Hello Meta2.__new__ "
        return super(Meta2, mcl).__new__(
            type, # looks to cause all strange behavior, but anyway pass type here, not mcl
            name, bases, attrs)

print "Declaring BaseClass1"
class BaseClass1(object):
    __metaclass__ = Meta1

print "-----------------------"
print "Declaring BaseClass2"
class BaseClass2(BaseClass1):
    __metaclass__ = Meta2

print "-----------------------"
print BaseClass2.__class__

它的输出:

Declaring BaseClass1
Hello Meta1.__new__ 
-----------------------
Declaring BaseClass2
Hello Meta2.__new__ 
Hello Meta1.__new__ # WHY WAS IT INVOKED?
-----------------------
<class '__main__.Meta1'>

关于代码的问题:

为什么类 BaseClass2 的定义没有任何问题,即使 BaseClass2 的 __metaclass__ 属性设置为 Meta2 并且其父类 BaseClass1 的 __metaclass__ 属性设置为 Meta1,Meta1 和 Meta2 都不是子类另一个班级?

为什么在 BaseClass2 定义中同时调用 Meta2.__new__Meta1.__new__

在什么情况下调用父类的元类中的方法?

长篇大论:

在尝试了解我们项目中的元类如何工作时,我编写了上面可以找到的代码。 (该项目使用 Python 2.7.3,看起来项目中使用的元类是合理的,因为它们用于向用户提供 API,并且元类在后台为用户做了很多事情。)

首先,我试图找到有关元类如何与继承一起工作的文档。 Guido van Rossum 的以下文章(相当陈旧但看起来对 Python 2.7 有效)阐明了在继承的情况下如何选择元类,对 subling 类的元类有什么要求以及可能的小技巧在为兄弟类选择元类时由 Python 执行:https://www.python.org/download/releases/2.2.3/descrintro/。我读过的关于 Python 元类的这篇文章和其他文章并没有解释我观察到的行为。我想阅读 Python 解释器代码会有所启发,但我相信文档的力量,并希望可以避免这种极端措施。欢迎对描述观察到的代码行为的材料提供任何答案/指针。

【问题讨论】:

我不知道为什么这会像您在 Python 2 中显示的那样工作,但由于元类冲突,BaseClass2 的定义导致 Python 3 中的 TypeError继承元类Meta1,但你告诉它使用不相关的元类Meta2)。我怀疑 Python 2 的行为在这种情况下被破坏了。 【参考方案1】:

看了很多遍,我想我找到了答案。 Python 3 文档中有一个 section 说明了这一点。

3.3.3.3。确定合适的元类

类定义的适当元类确定如下:

如果没有给出基数和显式元类,则使用type() 如果给出了一个明确的元类并且它不是type() 的实例,那么它是 直接用作元类 如果type() 的实例作为显式元类给出,或者定义了基类,则使用最派生的元类

从显式指定的元类(如果有)和所有指定基类的元类(即type(cls))中选择最派生的元类。最派生的元类是这些候选元类的所有的子类型。如果没有一个候选元类符合该标准,则类定义将失败并显示TypeError

我认为这仍然适用于 Python 2(无论如何是 v2.7),即使我在其文档中找不到与上述类似的内容。

BaseClass2 定义同时调用Meta2.__new__() Meta1.__new__() 的原因很简单——Meta2.__new__() 通过调用super() 显式调用它。但是,要使其正常工作,您还需要更改 Meta2.__new__(),使其返回 super(Meta2, mcl).__new__(mcl, name, bases, attrs) 而不是 super(Meta2, mcl).__new__(type, name, bases, attrs)

【讨论】:

以上是关于在啥情况下调用来自父类的多个元类?的主要内容,如果未能解决你的问题,请参考以下文章

swift 元类构造方法

继承和组合混搭的情况下,构造和析构函数的调用顺序

java里静态方法在啥情况下使用,好处是啥

为啥要用invoke方法换句话说就是在啥情况下使用invoke方法?

在啥情况下 viewWillAppear 会被调用?

在啥情况下调用__rmul__?