在哪种情况下调用父类中的多个元类?

时间:2015-08-11 16:09:05

标签: python inheritance python-2.x metaclass

奇怪的代码(使用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解释器代码将揭示亮点,但我相信文档的力量,并希望可以避免这种极端的措施。任何描述观察到的代码行为的材料的答案/指针都是受欢迎的。

1 个答案:

答案 0 :(得分:1)

环顾四周后,我想我找到了答案。 Python 3 文档中有一个section这样写。

  

3.3.3.3。确定适当的元类

     

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

     
      
  • 如果没有给出基数和显式元类,则使用type()
  •   
  • 如果给出了显式元类,并且它不是type()的实例,则为   直接用作元类
  •   
  • 如果给定type()的实例作为显式元类或定义了基数,则使用派生程度最高的元类
  •   
     

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

即使我在其文档中找不到类似于上述内容的东西,我仍然认为这同样适用于Python 2(v2.7)。

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