Python 以编程方式更新 YAML 文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23914370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 03:39:45  来源:igfitidea点击:

Update YAML file programmatically

pythonyaml

提问by Ignacio Verona

I've a Python dict that comes from reading a YAML file with the usual

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

yaml.load(stream)

I'd like to update the YAML file programmatically given a path to be updated like:

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

group1,option1,option11,value

组 1、选项 1、选项 11、值

and save the resulting dict again as a yaml file. I'm facing the problem of updating a dicntionary, taking into account that the path is dynamic (let's say a user is able to enter the path through a simple CLI I've created using Cmd).

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

Any ideas?

有任何想法吗?

thanks!

谢谢!

UPDATELet me be more specific on the question: The issue is with updating part of a dictionary where I do not know in advance the structure. I'm working on a project where all the configuration is stored on YAML files, and I want to add a CLI to avoid having to edit them by hand. This a sample YAML file, loaded to a dictionary (config-dict) using PyYaml:

更新让我更具体地说明这个问题:问题在于更新我事先不知道结构的字典的一部分。我正在处理一个所有配置都存储在 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

I've created the CLI with Cmd, and it's working great even with autocompletion. The user may provide a line like:

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

config port p1 mode amode

So, I need to edit:

所以,我需要编辑:

config-dict['config']['port']['p1']['mode'] and set it to 'amode'. Then, use yaml.dump() to create the file again. Another possible line would be:

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

config a-function enable

So config-dict['config']['a-function'] has to be set to 'enable'.

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

My problem is when updating the dictionary. If Python passed values as a reference would be easy: Just iterate through the dict until the right value is found and save it. Actually this is what I'm doing for the Cmd autocomplete. But I don't know how to do the update.

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

Hope I explained myself better now!

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

Thanks in advance.

提前致谢。

回答by Jan Vlcinsky

In fact the solution follows simple patter: load - modify - dump:

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

Before playing, be sure you have pyyaml installed:

在玩之前,请确保您已安装 pyyaml:

$ pip install pyyaml

testyaml.py

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)

Resulting data.yaml

结果 data.yaml

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

回答by Dan Temkin

Updating seems to the one place where pyyaml falls short. You cannot even use yaml.load on a file that was opened in (a)ppend mode without an exception. Now this may be a bit tedious for complex dictionaries but if each added item represents a separate case or document you could handle it as if it were any other text file.

更新似乎是 pyyaml 不足的地方。您甚至不能在 (a) ppend 模式下打开的文件上使用 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)

While this doesn't necessarily help with value updating, it does allow for file updating. You might also look into using json.dump on the file. I know it is in YAML but the formats are largely compatible unless you are using the python-object storage feature in YAML.

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

For a OS agnostic approach to carriage character assignment remember to use os.linesep.

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

Best of luck. Hope this helps.

祝你好运。希望这可以帮助。

回答by Jyoti Arora

Try out this method, i am using for updating yaml or json files. def update_dictionary_recursively(dictionary, key, value, key_separator="."): """Update givendictionarywith the givenkeyandvalue`.

试试这个方法,我用来更新 yaml 或 json 文件。 def update_dictionary_recursively(dictionary, key, value, key_separator="."): """Update given字典with the given键值and`。

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

`

`

回答by Fabio Caccamo

It is very simple to do it with python-benedict, a solid python dict subclass that support IO operations with many formats, including yaml.

这是非常简单的做到这一点的python-benedict,坚实的Python字典子类,与众多格式的支持IO操作,包括yaml

Installation: pip install python-benedict

安装: pip install python-benedict

You can initialize it directly from the yamlfile:

您可以直接从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)

Here the library repository and the documentation: https://github.com/fabiocaccamo/python-benedict

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

回答by ChaosPredictor

If you can do it with JSON, much easier.

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