繁体   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