为什么我不能更改类的 __metaclass__ 属性?
我有一个奇怪且不寻常的元类用例,我想在定义基类后更改它的 __metaclass__
,以便它的子类将自动使用新的 __metaclass__
>。但这奇怪的是不起作用:
class MetaBase(type):
def __new__(cls, name, bases, attrs):
attrs["y"] = attrs["x"] + 1
return type.__new__(cls, name, bases, attrs)
class Foo(object):
__metaclass__ = MetaBase
x = 5
print (Foo.x, Foo.y) # prints (5, 6) as expected
class MetaSub(MetaBase):
def __new__(cls, name, bases, attrs):
attrs["x"] = 11
return MetaBase.__new__(cls, name, bases, attrs)
Foo.__metaclass__ = MetaSub
class Bar(Foo):
pass
print(Bar.x, Bar.y) # prints (5, 6) instead of (11, 12)
我所做的很可能是不明智/不受支持/未定义的,但我一生都无法弄清楚旧元类是如何被调用的,而且我至少想明白这是怎么可能的。
编辑:根据jsbueno
提出的建议,我用以下行替换了Foo.__metaclass__ = MetaSub
行,这正是我所要做的通缉:
Foo = type.__new__(MetaSub, "Foo", Foo.__bases__, dict(Foo.__dict__))
I have a weird and unusual use case for metaclasses where I'd like to change the __metaclass__
of a base class after it's been defined so that its subclasses will automatically use the new __metaclass__
. But that oddly doesn't work:
class MetaBase(type):
def __new__(cls, name, bases, attrs):
attrs["y"] = attrs["x"] + 1
return type.__new__(cls, name, bases, attrs)
class Foo(object):
__metaclass__ = MetaBase
x = 5
print (Foo.x, Foo.y) # prints (5, 6) as expected
class MetaSub(MetaBase):
def __new__(cls, name, bases, attrs):
attrs["x"] = 11
return MetaBase.__new__(cls, name, bases, attrs)
Foo.__metaclass__ = MetaSub
class Bar(Foo):
pass
print(Bar.x, Bar.y) # prints (5, 6) instead of (11, 12)
What I'm doing may very well be unwise/unsupported/undefined, but I can't for the life of me figure out how the old metaclass is being invoked, and I'd like to least understand how that's possible.
EDIT: Based on a suggestion made by jsbueno
, I replaced the line Foo.__metaclass__ = MetaSub
with the following line, which did exactly what I wanted:
Foo = type.__new__(MetaSub, "Foo", Foo.__bases__, dict(Foo.__dict__))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
问题是继承时不使用 __metaclass__ 属性,这与您的预期相反。
Bar
也不会调用“旧”元类。文档对于如何找到元类有以下说明:因此,在
Bar
类中实际用作元类的内容是在父级的__class__
属性中找到的,而不是在父级的__metaclass__
属性中找到的。如需了解更多信息,请参阅此 StackOverflow 答案。
The problem is the
__metaclass__
attribute is not used when inherited, contrary to what you might expect. The 'old' metaclass isn't called either forBar
. The docs say the following about how the metaclass is found:So what is actually used as metaclass in your
Bar
class is found in the parent's__class__
attribute and not in the parent's__metaclass__
attribute.More information can be found on this StackOverflow answer.
类的元类信息在创建时使用(要么解析为类块,要么动态地通过显式调用元类)。它无法更改,因为元类通常在类创建时进行更改 - 创建的类类型是元类。它的 __metaclass__ 属性一旦创建就不再相关。
但是,可以创建给定类的副本,并让该副本具有与原始类不同的元类。
在您的示例中,如果不执行:
Foo.__metaclass__ = MetaSub
,而是执行:
Foo = Metasub("Foo", Foo.__bases__, dict(Foo.__dict__))
你将会达到你的目的。新的
Foo
的所有效果都与其前身相同,但具有不同的元类。但是,之前存在的
Foo
实例不会被视为新Foo
的实例 - 如果您需要,最好创建Foo
而是使用不同的名称进行复制。The metaclass information for a class is used at the moment it is created (either parsed as a class block, or dynamically, with a explicit call to the metaclass). It can't be changed because the metaclass usually does make changes at class creation time - the created class type is the metaclasse. Its
__metaclass__
attribute is irrelevant once it is created.However, it is possible to create a copy of a given class, and have the copy bear a different metclass than the original class.
On your example, if instead of doing:
Foo.__metaclass__ = MetaSub
you do:
Foo = Metasub("Foo", Foo.__bases__, dict(Foo.__dict__))
You will achieve what you intended. The new
Foo
is for all effects equal its predecessor, but with a different metaclass.However, previously existing instances of
Foo
won't be considered an instance of the newFoo
- if you need that, you better create theFoo
copy with a different name instead.子类使用其父类的__metaclass__。
您的用例的解决方案是对父类的 __metaclass__ 进行编程,以便父类与其子类具有不同的行为。也许让它检查类字典中的类变量并根据其值实现不同的行为
(这是 type 用于控制是否为实例提供字典的技术,具体取决于是否定义了 __slots__ )。
Subclasses use the __metaclass__ of their parent.
The solution to your use-case is to program the parent's __metaclass__ so that it will have different behaviors for the parent than for its subclasses. Perhaps have it inspect the class dictionary for a class variable and implement different behaviors depending on its value
(this is the technique type uses to control whether or not instances are given a dictionary depending on the whether or not __slots__ is defined).