【问题标题】:Update YAML file programmatically以编程方式更新 YAML 文件
【发布时间】:2014-07-17 20:08:15
【问题描述】:

我有一个 Python dict,它来自使用通常的方式读取 YAML 文件

yaml.load(stream)

我想以编程方式更新 YAML 文件,给定要更新的路径,例如:

组1,选项1,选项11,值

并将生成的 dict 再次保存为 yaml 文件。我面临更新字典的问题,考虑到路径是动态的(假设用户能够通过我使用 Cmd 创建的简单 CLI 输入路径)。

有什么想法吗?

谢谢!

更新 让我更具体地说明这个问题:问题在于更新我事先不知道结构的字典的一部分。我正在开发一个项目,其中所有配置都存储在 YAML 文件中,我想添加一个 CLI 以避免手动编辑它们。这是一个示例 YAML 文件,使用 PyYaml 加载到字典 (config-dict):

config:
 a-function: enable
 b-function: disable
 firewall:
  NET:
   A:
    uplink: enable
    downlink: enable
   B:
    uplink: enable
    downlink: enable
  subscriber-filter:
   cancellation-timer: 180
 service:
  copy:
   DS: enable
  remark:
   header-remark:
    DSC: enable
    remark-table:
 port:
  linkup-debounce: 300
  p0:
   mode: amode
  p1:
   mode: bmode
  p2:
   mode: amode
  p3:
   mode: bmode

我已经使用 Cmd 创建了 CLI,即使使用自动完成功能它也能很好地工作。用户可以提供如下一行:

config port p1 mode amode

所以,我需要编辑:

config-dict['config']['port']['p1']['mode'] 并将其设置为 'amode'。然后,使用 yaml.dump() 再次创建文件。另一个可能的行是:

config a-function enable

所以 config-dict['config']['a-function'] 必须设置为 'enable'。

我的问题是更新字典时。如果 Python 将值作为引用传递会很容易:只需遍历 dict 直到找到正确的值并保存它。实际上,这就是我为 Cmd 自动完成所做的事情。但我不知道如何进行更新。

希望我现在能更好地解释自己!

提前致谢。

【问题讨论】:

  • 保存到 YAML 很容易yaml.dump,但您要求设计您的应用程序。这太难了,基于意见,实际上与 YAML 没有太大关系。
  • @JanVlcinsky 感谢您的回答。你是对的,我问的更多是关于 Python 而不是 YAML 本身。这与设计无关,而是一个非常具体的问题:如何更新具有动态大小的字典。
  • 呃,我在这里没有看到问题 - 真正直接的方法是解析 cli 上给出的路径,遍历字典以确保路径有效,更改值,转储.问题出在哪里?除此之外 - 你试过什么,什么没用,粘贴一些代码。

标签: python yaml


【解决方案1】:

事实上,解决方案遵循简单的模式:加载 - 修改 - 转储:

在玩之前,请确保你已经安装了 pyyaml:

$ pip install pyyaml

testyaml.py

import yaml
fname = "data.yaml"

dct = {"Jan": {"score": 3, "city": "Karvina"}, "David": {"score": 33, "city": "Brno"}}

with open(fname, "w") as f:
    yaml.dump(dct, f)

with open(fname) as f:
    newdct = yaml.load(f)

print newdct
newdct["Pipi"] = {"score": 1000000, "city": "Stockholm"}

with open(fname, "w") as f:
    yaml.dump(newdct, f)

结果data.yaml

$ cat data.yaml
David: {city: Brno, score: 33}
Jan: {city: Karvina, score: 3}
Pipi: {city: Stockholm, score: 1000000}

【讨论】:

  • 谢谢@Jan。这里的问题是更新路径不固定。根据用户的输入,我可能正在更新 dict['A']['a1']['a11'] 或 dict['A']['a2']['a21']['a221'] .所以,我试图弄清楚如何根据“关键路径”(比如'A,a1,a11')循环遍历字典并更新该值。
  • @IgnacioVerona 要使用动态键数修改字典,请参阅stackoverflow.com/questions/17462011/…
  • 适用于插入,但更新呢?如果 python 允许通过引用,这将很容易,但因为它没有,所以对我来说有点挑战!
【解决方案2】:

使用python-benedict 非常简单,这是一个可靠的python dict 子类,支持多种格式的IO 操作,包括yaml

安装:pip install python-benedict

可以直接从yaml文件初始化:

from benedict import benedict

f = 'data.yaml'
d = benedict.from_yaml(f)
d['Pipi'] = {'score': 1000000, 'city': 'Stockholm'}

# benedict supports keypath (dot syntax by default),
# so it's possible to update nested values easily:
d['Pipi.score'] = 2000000
print(d['Pipi']) # -> {'score': 2000000, 'city': 'Stockholm'}

d.to_yaml(filepath=f)

这里是库存储库和文档: https://github.com/fabiocaccamo/python-benedict

注意:我是这个项目的作者

【讨论】:

    【解决方案3】:

    更新似乎是 pyyaml 不足的地方。您甚至不能对以(a)追加模式打开的文件使用 yaml.load ,而不会出现异常。现在这对于复杂的字典可能有点乏味,但如果每个添加的项目代表一个单独的案例或文档,您可以像处理任何其他文本文件一样处理它。

    newinfo = {"Pipi": {"score": 100000, "city": "Stockholm"}}
    with open(fname, "a") as f:
         sep = "\n" # For distinct documents use "\n...\n" as separator
    
         # Pay attention to where you put the separator. 
         # if the file exists and is in traditional format place at 
         # beginning of string. else place at the end.
    
         infostring = "{}".format(newinfo)
         f.write(infostring + sep)
    

    虽然这不一定有助于更新值,但它确实允许文件更新。您也可以考虑在文件上使用 json.dump。我知道它在 YAML 中,但格式在很大程度上是兼容的,除非您在 YAML 中使用 python-object 存储功能。

    对于与操作系统无关的回车字符分配方法,请记住使用 os.linesep。

    祝你好运。希望这会有所帮助。

    【讨论】:

    • 如果在上下文管理器退出后对 f 调用 close 的话,这里会有一点冗余。不确定刷新调用,但我认为上下文管理器也会处理这个问题。
    • 感谢您的关注。老实说,flush() 在这一点上只是一种习惯的力量。而且我总是忘记是否/在哪里应该有一个close()。再次感谢。
    【解决方案4】:

    试试这个方法,我用来更新 yaml 或 json 文件。 def update_dictionary_recursively(dictionary, key, value, key_separator="."): """Update givendictionarywith the givenkeyandvalue`。

    if dictionary contains value as dict E.g. {key1:value1, key2:{key3, {key4:value4}}} and you have to
    update key4 then `key` should be given as `key2.key3.key4`.
    
    If dictionary contains value as list E.g. {key1:{key2:[{key3:valie3}, {key4:value4}]}} and you have to update
    key4 then `key` should be given as `key1.key2[1].key4`.
    
    :param dictionary: Dictionary that is to be updated.
    :type dictionary: dict
    :param key: Key with which the dictionary is to be updated.
    :type key: str
    :param value: The value which will be used to update the key in dictionary.
    :type value: object
    :param key_separator: Separator with which key is separated.
    :type key_separator str
    :return: Return updated dictionary.
    :rtype: dict
    """
    index = key.find(key_separator)
    if index != -1:
        current_key = key[0:index]
        key = key[index + 1:]
        try:
            if '[' in current_key:
                key_index = current_key.split('[')
                current_key = key_index[0]
                list_index = int(key_index[1].strip(']'))
                dictionary[current_key][list_index] = update_dictionary_recursively(
                    dictionary[current_key][list_index], key, value, key_separator)
            else:
                dictionary[current_key] = update_dictionary_recursively(dictionary[current_key],
                                                                                        key, value, key_separator)
        except (KeyError, IndexError):
            return dictionary
    else:
        if '[' in key:
            key_index = key.split('[')
            list_index = int(key_index[1].strip(']'))
            if list_index > len(dictionary) - 1:
                return dictionary
            dictionary[list_index] = value
        else:
            if key not in dictionary:
                return dictionary
            dictionary[key] = value
    return dictionary
    

    `

    【讨论】:

      【解决方案5】:

      如果你可以用 JSON 来做,那就容易多了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-19
        • 1970-01-01
        • 2015-03-27
        • 2014-03-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多