python - 在什么情况下调用父类的多个元类?

标签 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 有效)阐明了在继承的情况下如何选择元类,子类的元类的要求是什么以及可以使用的小技巧在为兄弟类选择元类时由 Python 执行:https://www.python.org/download/releases/2.2.3/descrintro/ .我在 Python 中阅读的关于元类的这篇文章和其他文章并没有解释我观察到的行为。我想阅读 Python 解释器代码会有所启发,但我相信文档的力量,并希望可以避免这种极端措施。欢迎对描述观察到的代码行为的 Material 提供任何答案/指针。

最佳答案

经过大量的环顾四周,我想我找到了答案。 Python 3 文档有一个 section就是这么说的。

3.3.3.3. Determining the appropriate metaclass

The appropriate metaclass for a class definition is determined as follows:

  • if no bases and no explicit metaclass are given, then type() is used
  • if an explicit metaclass is given and it is not an instance of type(), then it is used directly as the metaclass
  • if an instance of type() is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used

The most derived metaclass is selected from the explicitly specified metaclass (if any) and the metaclasses (i.e. type(cls)) of all specified base classes. The most derived metaclass is one which is a subtype of all of these candidate metaclasses. If none of the candidate metaclasses meets that criterion, then the class definition will fail with 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)

关于python - 在什么情况下调用父类的多个元类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31946790/

相关文章:

python - 为什么我得到 "ufunc ' multiply' did not contain a loop with signature matching types dtype ('S32' ) dtype ('S32' ) dtype ('S32' )"with values from raw_input

python - 在 Django 应用程序中提供静态网页的最佳方式是什么?

python - 尝试使用 Python 3.7.2 和 IMAPClient 批量删除电子邮件 - imaplib.IMAP4.error : UID command error: BAD [b'Command line too large']

c++ - C++中公共(public)继承基类调用派生类的私有(private)虚函数

java - OOP:向上转换与直接实例化

python - 在python中重新排序固定长度数组

python - 我想知道我关于使用 keras 实现 lstm 层是否正确

Python如何从字符串中删除转义字符

c++ - '{' token c++ 继承问题之前的另一个预期类名

python - 使用 split 方法预处理文本文件中的数据