在我的代码中,我使用JSON文件来决定用于初始化对象的值。
在我的config.json
:
{
"ClassA": {
"p1": 3,
"p2"; 4
}
}
我的ClassA
:
class ClassA:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
然后我通过:
实例化ClassA
with open("config.json", "r") as f:
config = json.load(f)
a = ClassA(**config["ClassA"])
现在问题出现在与值相关联的键发生变化时(例如" p1"更改为" p3");或ClassA
中参数的名称发生变化。因为任何更改都意味着使用上面的实例化方法失败。
为了尝试将配置与我的代码分离,我已经定义了另一个类来将配置参数名称映射到我的类参数名称,反之亦然。
class IInitializable(ABC):
_parameter_name_lookup = dict()
@classmethod
def parameter_name_lookup(cls, mode):
if mode == "ConfigurationToClass":
return {y: x for x, y in cls._parameter_name_lookup.items()}
elif mode == "ClassToConfiguration":
return cls._parameter_name_lookup
else:
raise NameError("No such mode")
现在说我将config.json
更改为:
{
"ClassA": {
"p1": 3,
"p9"; 4
}
}
我的ClassA
:
class ClassA:
def __init__(self, p1, p4):
self.p1 = p1
self.p2 = p4
我会让ClassA
继承IInitializeable
并定义映射:
class ClassA(IInitializable):
_parameter_name_lookup = {"p4": "p9"}
def __init__(self, p1, p4):
self.p1 = p1
self.p2 = p4
然后,我可以使用新的ClassA
初始化我的config.json
:
with open("config.json", "r") as f:
config = json.load(f)
config_to_class_lookup = ClassA.parameter_name_lookup("ConfigurationToClass")
new_parameters_for_a = dict()
for config_parameter_name, value in config.items():
class_parameter_name = config_to_class_lookup.get(config_parameter_name, config_parameter_name)
new_parameters_for_a[class_parameter_name] = value
a = ClassA(**new_parameters_for_a)
尽管如此,我确信有更优雅的方法可以解决这个问题。我已经读过我的元类和装饰器了,似乎(?)就是这样的方向。但我似乎无法将所有部分组合在一起。
答案 0 :(得分:0)
我认为你这太复杂了。
更改API应该是您很少做的事情,而不是所有时间。
您可能已经需要对配置文件进行版本设置,以便了解它们的设计版本。
因此,如果您编写从早期JSON模式迁移到当前模式的代码,则可以在load
时执行此操作。然后,您的所有类只需要担心如何从当前版本初始化。 (并且据推测,如果他们还需要保存,你永远不需要保存旧版本,使问题的一半不存在。)
从一个JSON模式版本迁移到另一个版本是一个常见问题,已知的解决方案和大量库可以帮助您。 (更常见的数据库迁移问题甚至是更多常见且众所周知。)
即使您想自己编写它也相对容易 - 不需要元类或装饰器或任何其他东西,只需根据硬编码规则递归行走和转换嵌套的dict / list结构(用于快速和脏迁移)或者在某种转换字典中查找(为了更清洁的解决方案)。
另外,这使您可以轻松地为迁移编写单元测试,而不需要在所有类的所有版本的笛卡尔积之间进行一些复杂的集成测试。