簡體   English   中英

為什么我不能更改類的__metaclass__屬性?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM