在啥情况下调用来自父类的多个元类?
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)
。
【讨论】:
以上是关于在啥情况下调用来自父类的多个元类?的主要内容,如果未能解决你的问题,请参考以下文章