簡體   English   中英

具有類變量繼承的子類

[英]Subclass with class variable inheritance

在父類中,我定義了一個類變量和一個用於修改類變量值的類方法。 我希望每個子類都使用其自己的變量,而不是與其父級共享。

但是結果卻出乎我的意料。 在以下示例中,我有兩組父類和子類,以及一些代碼來演示出了什么問題:

class P:
    _X = 0

    @classmethod
    def cm(cls):
        print("In p cm")
        cls._X += 1

class C1(P):
    pass

class C2(P):
    pass

class Image:
    _callbacks = {}

    @classmethod
    def registerDataFormat(cls, fmt, loader):
        if fmt in cls._callbacks.keys():
            print("The %s format has already been registered." % (fmt))
            return False

        cls._callbacks[fmt] = {}
        cls._callbacks[fmt]["loader"] = loader

class HSImage(Image):
    pass

class GT(Image):
    pass

if __name__ == '__main__':
    C1.cm()
    print(C1._X)
    print(P._X)
    C2.cm()
    print(C2._X)
    print(P._X)

    HSImage.registerDataFormat("mat", "loader 1")
    print(HSImage._callbacks)
    print(Image._callbacks)
    GT.registerDataFormat("mat", "loader 2")
    print(GT._callbacks)
    print(Image._callbacks)

結果如下:

In p cm
1
0
In p cm
1
0
{'mat': {'loader': 'loader 1'}}
{'mat': {'loader': 'loader 1'}}
The mat format has already been registered.
{'mat': {'loader': 'loader 1'}}
{'mat': {'loader': 'loader 1'}}

第一個示例具有預期的結果,但沒有第二個示例,當我在第二組類的子類上調用類方法時,為什么類變量與父類共享?

我的預期結果:

In p cm
1
0
In p cm
1
0
{'mat': {'loader': 'loader 1'}}
{}
{'mat': {'loader': 'loader 2'}}
{}

不同之處在於您對字典進行了變異 第一個簡單的整數示例適用於不可變的整數對象。 cls._X += 1_X的值(如有必要,從父類獲取),此后old + 1操作生成一個新的整數對象,然后將其分配回cls._X 作業在這里很重要,因為這將在子班上進行。

但是在第二種情況下,您沒有將任何東西分配回類:

cls._callbacks[fmt] = {}
cls._callbacks[fmt]["loader"] = loader

你分配到字典的一個關鍵。 cls._callbacks屬性本身未更改,它是所有類之間共享的同一詞典。 查找在Image基類上找到的cls._callbacks引用,此后,通過添加鍵值對來更新字典本身。 子類( HSImageGT )都沒有屬性本身。

您將需要創建一個副本並分配回更改后的副本:

cls._callbacks = {k: dict(v) for k, v in cls._callbacks.items()}
cls._callbacks[fmt] = {'loader': loader}

這不僅會創建外部字典的副本,還會創建所有值的副本,因為在為fmt添加新字典之前,這些值也都是字典。 然后將該副本分配給cls._callbacks ,從而有效地在子類上創建新屬性(如果尚未存在)。

當然那不是那么有效。 每次注冊裝載程序時都會創建一個副本。 您最好在每個子類上創建一個新的_callback字典對象,但這會很乏味並且很容易被忘記。 您可以改為使用__init_subclass__方法 自動化__init_subclass__

class Image:
    def __init_subclass__(cls):
        cls._callbacks = {}

    @classmethod
    def registerDataFormat(cls, fmt, loader):
        if fmt in cls._callbacks:
            print("The {} format has already been registered.".format(fmt))
            return

        cls._callbacks[fmt] = {'loader': loader}

為您創建的每個子類調用__init_subclass__方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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