![](/img/trans.png)
[英]Why protobuf generates python class with __metaclass__ attribute?
[英]Why can't I change the __metaclass__ attribute of a class?
我有一個奇怪的和不尋常的元類用例,我想在定義它之后更改基類的__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__))
問題是繼承時不使用__metaclass__
屬性,這與您的預期相反。 對於Bar
不會調用“舊的”元類。 文檔說明了如何找到元類:
適當的元類由以下優先規則確定:
- 如果dict ['__ metaclass__']存在,則使用它。
- 否則,如果至少有一個基類,則使用其元類(這首先查找__class__屬性,如果未找到,則使用其類型)。
- 否則,如果存在名為__metaclass__的全局變量,則使用它。
- 否則,使用舊式的經典元類(types.ClassType)。
因此,在父類的__class__
屬性中找到實際用作Bar
類__metaclass__
類的內容,而不是在父類的__metaclass__
屬性中。
可以在此StackOverflow答案中找到更多信息。
類的元類信息在創建時使用(解析為類塊,或動態地,通過對元類的顯式調用)。 它無法更改,因為元類通常會在類創建時進行更改 - 創建的類類型是metaclasse。 它的__metaclass__
屬性一旦創建就無關緊要了。
但是,可以創建給定類的副本,並使副本具有與原始類不同的元類。
在你的例子中,如果不是這樣做:
Foo.__metaclass__ = MetaSub
你做:
Foo = Metasub("Foo", Foo.__bases__, dict(Foo.__dict__))
你將實現你的目標。 新的Foo
適用於所有效果,與其前身相同,但具有不同的元類。
但是,之前現有的Foo
實例不會被視為新Foo
的實例 - 如果需要,最好使用不同的名稱創建Foo
副本。
子類使用其父級的__metaclass__ 。
您的用例的解決方案是對父級的__metaclass__進行編程,以使其對父級具有與其子類不同的行為。 也許讓它檢查類字典中的類變量,並根據其值實現不同的行為(這是技術類型用於控制是否根據是否定義了__slots__來為實例提供字典)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.