我想创建一组类来管理不同实验的不同配置参数。我想为每个类设置一个属性列表作为类属性,以检查是否确实需要给定的属性。
为了保存代码,我编写了一个通用的__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
答案 0 :(得分:4)
__class__
只会指向您在其上定义方法的类。 目的是不随子类更改的。
如果要获取当前实例的类,请使用type()
function(例如type(self)
)。在这种情况下,它返回self.__class__
,但知道type()
知道如何处理不同类型的对象,而不仅仅是Python类。可能是您一直打算使用self.__class__
。
除非您特别想访问定义了方法的类对象,而忽略继承,然后仅使用明确的注释来解释为什么这样做,否则我不会使用__class__
。 __class__
闭包尚未广为人知,也不打算用于一般用途。
来自reference documentation on class creation:
__class__
是由编译器创建的隐式闭包引用,如果类主体中的任何方法引用__class__
或super
。这允许super()
的零参数形式根据词法作用域正确地标识正在定义的类,而用于进行当前调用的类或实例是根据传递给该方法的第一个参数来标识的。 / p>
对于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',))