Python:带字符串插值的动态 json

Python: dynamic json with string interpolation

我创建了一个 class 函数来提供一些云基础设施。

response = self.ecs_client.register_task_definition(
containerDefinitions=[
                {
                    "name": "redis-283C462837EF23AA",
                    "image": "redis:3.2.7",
                    "cpu": 1,
                    "memory": 512,
                    "essential": True,
                },
...

这篇很长json,我只展示了开头

然后我重构代码以使用参数而不是硬编码哈希、内存和 cpu。

response = self.ecs_client.register_task_definition(
containerDefinitions=[
                {
                    "name": f"redis-{git_hash}",
                    "image": "redis:3.2.7",
                    "cpu": {num_cpu},
                    "memory": {memory_size},
                    "essential": True,
                },
...

我在这段代码之前从配置文件中读取了 git_hashnum_cpumemory_size 的值。

现在,我还想从文件中读取整个 json。

问题是如果我在文件中保存 {num_cpu} 等,字符串插值将不起作用。

如何从我的逻辑中提取 json 并仍然使用字符串插值或变量?

您可以使用 string 中的 Template

{
    "name": "redis-${git_hash}",
    "image": "redis:3.2.7",
    "cpu": ${num_cpu},
    "memory": ${memory_size},
    "essential": true
}
from string import Template
import json

if __name__ == '__main__':
    data = dict(
        num_cpu = 1, 
        memory_size = 1,
        git_hash = 1
    )
    with open('test.json', 'r') as json_file:
        content = ''.join(json_file.readlines())
        template = Template(content)
        configuration = json.loads(template.substitute(data))
        print(configuration)

# {'name': 'redis-1', 'image': 'redis:3.2.7', 'cpu': 1, 'memory': 1, 'essential': True}

意见:我认为总体做法是错误的。这种方法不如其他方法受欢迎是有原因的。您可以将您的配置分成两个文件(1)静态选项列表和(2)紧凑的可变配置,并将它们组合到您的代码中。

编辑:您可以创建一个从标准(静态或可变)JSON 文件FileConfig 读取配置的对象。然后使用另一个对象组合它们,比如行 ComposedConfig.

这将允许您扩展行为,并在混合中添加,例如,run-time 配置。这样,JSON 文件中的配置不再依赖于 run-time 参数,并且您可以将系统中的可变内容与静态内容分开。

PS:get方法只是一个解释组合行为的例子;你可以使用其他 methods/designs.

import json
from abc import ABC, abstractmethod 


class Configuration(ABC):
    
    @abstractmethod
    def get(self, key: str, default: str) -> str:
        pass


class FileConfig(Configuration):

    def __init__(self, file_path):
        self.__content = {}
        with open(file_path, 'r') as json_file:
            self.__content = json.load(json_file)
            
    def get(self, key: str, default: str) -> str:
        return self.__content.get(key, default)


class RunTimeConfig(Configuration):
    def __init__(self, option: str):
        self.__content = {'option': option}
    
    def get(self, key: str, default: str) -> str:
        return self.__content.get(key, default)


class ComposedConfig:

    def __init__(self, first: Configuration, second: Configuration):
        self.__first = first
        self.__second = second

    def get(self, key: str, default: str) -> str:
        return self.__first.get(key, self.__second.get(key, default))


if __name__ == '__main__':
    static = FileConfig("static.json")
    changeable = FileConfig("changeable.json")
    runTime = RunTimeConfig(option="a")
    config = ComposedConfig(static, changeable)
    alternative = ComposedConfig(static, runTime)
    print(config.get("image", "test")) # redis:3.2.7
    print(alternative.get("option", "test")) # a