Python/JSON:合并默认配置和用户配置

ju.*_*ju. 3 python configuration json

我在 python 应用程序中使用 JSON 配置文件。我想要一个带有层次结构的默认配置。
用户配置应仅包含与默认值不同的参数。我当前的解决方案是:

# default.json
{
    "parA" : 1,
    "parB" : {
       "sparA" : 0,
       "sparB" : 1
    }
}`

# user.json
{
    "parB" : {
       "sparB" : 9
    }
}
Run Code Online (Sandbox Code Playgroud)

配置是通过加载的

default_json = open("default.json")
default_config = json.load(default_json)
default_json.close()


user_json = open("user.json")
user_config = json.load(user_json)
user_json.close()  

config = default_config.copy()
config.update(user_config)
Run Code Online (Sandbox Code Playgroud)

这样做的问题是,整个密钥“parA”被覆盖,因此“sparA”被删除。有没有一种简单的方法可以只覆盖子键而不覆盖整个父键?

Ale*_*lli 5

这是可以做到的,尽管有点微妙。以下假设结构兼容性(例如,您不尝试将非字典合并到字典等),并且在非字典的情况下,“合并”是一个简单的替换。

它还假设就地修改基础对象是可以的(看起来确实如此,因为您无论如何都在更新副本;如果后者不是这种情况,那就没什么大不了的,只需添加一个复制语句,例如base_obj = dict(base_obj)for循环)...:

def selective_merge(base_obj, delta_obj):
    if not isinstance(base_obj, dict):
        return delta_obj
    common_keys = set(base_obj).intersection(delta_obj)
    new_keys = set(delta_obj).difference(common_keys)
    for k in common_keys:
        base_obj[k] = selective_merge(base_obj[k], delta_obj[k])
    for k in new_keys:
        base_obj[k] = delta_obj[k]
    return base_obj
Run Code Online (Sandbox Code Playgroud)

这适用于您给定的示例,但如果例如您给定的(default.json将非字典合并到字典中违反了结构兼容性约束;我不确定您想要什么),则会中断user.json'{"parB": 23}'这样的情况下发生什么)一个案例)。

因此,一旦您将函数调整为您的确切规格,您所需要做的就是在给定的代码中替换:

config.update(user_config)
Run Code Online (Sandbox Code Playgroud)

config = selective_merge(config, user_config)
Run Code Online (Sandbox Code Playgroud)