簡體   English   中英

如何在基類的方法中訪問子類的重寫的類屬性? '__class__'仍然指向基類

[英]How to access a child class's overridden class property in a base class's method? '__class__' still point to the base class

我想創建一組類來管理不同實驗的不同配置參數。 我想為每個類設置一個屬性列表作為類屬性,以檢查是否確實需要給定的屬性。

為了保存代碼,我編寫了一個通用的__init__ ,希望它可以應用於派生類並使用派生類的_attr_進行檢查工作。

我使用__class__來引用當前類,但它似乎指向基類。

這是一些代碼。 BCDConfig固有的__init__函數堅持認為_class__應該是在ExpConfig中定義的類。

import json

class ExpConfig:
    _attr_ = ['attr1', 'attr2']   # list of string

    def __init__(self, config):
        self.config = {}

        # only take defined attributes
        for key in config:
            if key in __class__._attr_:
                self.config[key] = config[key]
            else:
                raise ValueError

        # check if there is anything undefined
        for key in __class__._attr_:
            assert key in self.config, "Missing arguments!"

    def save_config(self, path):
        with open(path, 'w') as f:
            json.dump(self.config, f)

    @classmethod
    def load_config(cls, path):
        with open(path, 'r') as f:
            config = json.load(f)
        exp_config = __class__(config)
        return exp_config


class BCDConfig(ExpConfig):

    _attr_ = ['attr1', 'attr2', 'attr3']

    def __init__(self, config):
        super(BCDConfig, self).__init__(config)


if __name__ == '__main__':
    bcd_config1 = BCDConfig({'attr1':123, 'attr2':444})
    bcd_config1.save_config('./bcd1.cfg')
    print(BCDConfig.load_config('./bcd1.cfg').config)

    bcd_config2 = BCDConfig({'attr1':1253, 'attr2':4344, 'attr3':1})
    bcd_config2.save_config('./bcd2.cfg')
    print(BCDConfig.load_config('./bcd2.cfg'))

這是輸出。 我想知道是否有除__class__其他方法可以動態地解釋為派生類。 感謝您的任何幫助!

{'attr1': 123, 'attr2': 444}
Traceback (most recent call last):
  File "C:/Users/MysriO/Documents/Local Codes/DEAP_Ricotta/exp_config.py", line 46, in <module>
    bcd_config2 = BCDConfig({'attr1':1253, 'attr2':4344, 'attr3':1})
  File "C:/Users/MysriO/Documents/Local Codes/DEAP_Ricotta/exp_config.py", line 37, in __init__
    super(BCDConfig, self).__init__(config)
  File "C:/Users/MysriO/Documents/Local Codes/DEAP_Ricotta/exp_config.py", line 14, in __init__
    raise ValueError
ValueError

__class__只會指向您在其上定義方法的類。 目的是不隨子類一起更改。

如果要獲取當前實例的類,請使用type()函數 (例如type(self) )。 在這種情況下,它將返回self.__class__ ,但是知道type()知道如何處理不同類型的對象,而不僅僅是Python類。 可能是您一直打算使用self.__class__

除非您特別想訪問定義了方法的類對象,而忽略繼承,然后僅使用明確的注釋來解釋為什么這樣做,否則我不會使用__class__ __class__閉包尚未廣為人知,也不打算用於一般用途。

有關類創建參考文檔中

如果類主體中的任何方法引用__class__super__class__是編譯器創建的隱式閉包引用。 這允許super()的零參數形式根據詞法作用域正確識別正在定義的類,而用於進行當前調用的類或實例是根據傳遞給該方法的第一個參數來識別的。

對於load_config類方法,您已經具有對該類的引用:在此處使用cls ,而不是__class__

接下來,您實際上不需要 __init__的類引用。 您可以使用self._attr_代替; 可以通過實例訪問類屬性,前提是沒有實例屬性遮蓋它們:

class ExpConfig:
    _attr_ = ['attr1', 'attr2']   # list of string

    def __init__(self, config):
        self.config = {}

        # only take defined attributes
        for key in config:
            if key in self._attr_:
                self.config[key] = config[key]
            else:
                raise ValueError

        # check if there is anything undefined
        for key in self._attr_:
            assert key in self.config, "Missing arguments!"

self._attr_引用將在給定實例的正確類上找到_attr_屬性:

>>> class ExpConfig:
...     _attr_ = ['attr1', 'attr2']
...
>>> class BCDConfig(ExpConfig):
...     _attr_ = ['attr1', 'attr2', 'attr3']
...
>>> ExpConfig()._attr_
['attr1', 'attr2']
>>> BCDConfig()._attr_
['attr1', 'attr2', 'attr3']

我實際上將_attr_ 設置為對象 ,而不是列表。 屬性名稱必須是唯一的,不需要特定的順序,並且集已針對成員資格測試和交集進行了優化。 如果將集合與字典視圖結合使用,則可以快速測試丟失和多余的鍵:

class ExpConfig:
    _attr_ = frozenset(('attr1', 'attr2'))   # immutable set of strings

    def __init__(self, config):
        extra = config.keys() - self.attrs
        if extra:
            raise ValueError(f"Invalid config keys: {', '.join(extra)}")
        missing = self.attrs - config.keys()
        if missing:
            raise ValueError(f"Missing config keys: {', '.join(missing)}")

        self.config = config.copy()

    def save_config(self, path):
        with open(path, 'w') as f:
            json.dump(self.config, f)

    @classmethod
    def load_config(cls, path):
        with open(path, 'r') as f:
            config = json.load(f)
        return cls(config)

我使用了frozenset()對象,這是一個不可變的集合,因為您可能不想在創建類后更改屬性名稱。 frozenset()可以保護您免受意外錯誤的影響。

最后,子類可以使用set Union運算符重用父類的定義| ,因此BCDConfig可以定義為:

class BCDConfig(ExpConfig):
    _attr_ = ExpConfig._attr_ | frozenset(('attr3',))

暫無
暫無

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

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