如何在基类的方法中访问子类的重写类属性__类仍然指向基类

2024-10-01 07:26:44 发布

您现在位置:Python中文网/ 问答频道 /正文

我想创建一组类来管理不同实验的不同配置参数。我想为每个类设置一个属性列表作为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

Tags: pathkeyinselfconfiginitdefload
1条回答
网友
1楼 · 发布于 2024-10-01 07:26:44

__class__只指向在其上定义方法的类。它的目的是不随子类而改变。你知道吗

如果要获取当前实例的类,请使用^{} function(例如type(self))。在本例中返回self.__class__,但是要知道type()知道如何处理不同类型的对象,而不仅仅是Python类。可能你一直想用self.__class__。你知道吗

我不会使用__class__,除非您特别希望访问定义了方法的类对象,忽略继承,然后只使用显式注释来解释为什么要这样做。__class__闭包并不广为人知,也不打算用于一般用途。你知道吗

reference documentation on class creation

__class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__ or super. This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.

对于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_做成set object,而不是一个列表。属性名称必须是唯一的,不需要特定的顺序,并且集合针对成员资格测试和交集进行了优化。如果将集合与dictionary views组合,则可以快速测试缺少的键和无关键:

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 operator ^{}重用父类的定义,因此BCDConfig可以定义为:

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

相关问题 更多 >