添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在python中读取YAML文件并通过匹配键值对来访问数据

4 人关注

我正在使用Python开发一个软件,我需要读取一个有多个层次的YAML文件,如下所示。

#Filename: SampleCase.yml
%YAML 1.1
VesselTypes:
  - Name: Escort Tug
    Length: 32
    Breadth: 12.8
    Depth: 9
    Draughts:
    - Name: Draught1
      Mass: 500
      CentreOfGravity: [16.497, 0, 4.32]
    TowingStaples:
    - Name: Staple1
      Position: [0, 0, 0]
    Thrusters:
    - Name: Port Propeller
      Position: [0, -1, 0]
      MaxRPM: 1800
      MaxPower: 2525
    - Name: Stbd Propeller
      Position: [0, 1, 0]
      MaxRPM: 1800
      MaxPower: 2525
  - Name: Ship    
Vessels:
  - Name: Tug
    VesselType: Escort Tug
    Draught: Draught1
    InitialPosition: [0, 0, 0]
    Orientation: [0, 0, 0]
  - Name: Tanker
    VesselType: Ship
    Draught: Draught1
    InitialPosition: [0, 0, 0]
    Orientation: [0, 0, 0]
    Speed: 8  

在这里,有两艘名为 "拖船 "和 "油轮 "的船只。它们属于两种船舶类型,"护航拖船 "和 "船舶"。

#Filename: main.py
import yaml
# Reading YAML data
file_name = 'SampleCase.yml'
with open(file_name, 'r') as f:
    data = yaml.load(f)
print(data["Vessels"][0]["Name"])

我能够使用索引号(例如:data["Vessels"][0]["Name"])访问存储的数据,但是我想使用匹配的键来访问它们。例如,我想打印名为 "Tug "的船只的港口螺旋桨的MaxRPM值。在python中这样做的标准方法是什么?

1 个评论
如果是常规任务,你可以考虑将数据转换为JSON或XML,并使用JSONPath或XPath。
python
yaml
Amitava
Amitava
发布于 2016-12-10
3 个回答
Anthon
Anthon
发布于 2016-12-10
已采纳
0 人赞同

目前还没有一个标准的方法,这在很大程度上是由于YAML的键可能是复杂的。这使得对JSON等简单得多的格式有效的路径匹配方法无法使用。

如果你的YAML是 "无标签 "的,像你一样,它仍然允许比JSON更复杂的结构,但你可以相当容易地实现在YAML文件的集合类型(序列和映射)上递归行走,并在这样做时明确地匹配索引或键和/或元素或值。

import ruamel.yaml as yaml
def _do_not_care():
def find_collection(d, key=_do_not_care, value=_do_not_care, results=None):
    def check_key_value(d, k, v, results):
        # print('checking', key, value, k, d[k], results)
        if k == key:
            if value in [_do_not_care, v]:
                results.append(d)
                return
        elif key == _do_not_care and v == value:
            results.append(d)
            return
        if isinstance(v, (dict, list)):
            find_collection(v, key, value, results)
    if results is None:
        results = []
    if isinstance(d, dict):
        for k in d:
            check_key_value(d, k, d[k], results)
    if isinstance(d, list):
        for k, v in enumerate(d):
            check_key_value(d, k, v, results)
    return results
def find_first(d, key=_do_not_care, value=_do_not_care):
    ret_val = find_collection(d, key, value)
    return ret_val[0] if ret_val else {}
def find_value_for_key(d, key):
    return find_first(d, key)[key]

在上述情况下,你可以做到。

file_name = 'SampleCase.yml'
with open(file_name, 'r') as f:  
    data = yaml.safe_load(f)
for d in find_collection(data, value='Tug'):
    vessel_type = find_first(data, key='Name', value=d['VesselType'])
    port_propeller = find_first(vessel_type, key='Name', value='Port Propeller')
    print('Tug -> MaxRPM', find_value_for_key(port_propeller, key='MaxRPM'))

这个打印(假设输入已被纠正,见第1点)。

Tug -> MaxRPM 1800

然而,可能根本就没有必要指定该指令。PyYAML在七年后仍然不支持YAML 1.2,而且你的YAML似乎没有任何YAML 1.1的特性。

  • 你在使用PyYAML的load(),没有Loader参数,如果你不能控制输入,这可能是不安全的。如果可以的话,你应该总是使用safe_load(比如你的源码)。

  • 上述情况用以下方法进行了测试ruamel.yaml(PyYAML的超集,支持YAML 1.2以及1.1。声明:我是该软件包的作者)。如果你必须坚持使用PyYAML,我应该按原样工作。

    lucasnadalutti
    lucasnadalutti
    发布于 2016-12-10
    0 人赞同

    把你的 list 变成一个 dict ,其中的钥匙就是名字。

    result = {}
    for elem in data['Vessels']:
        name = elem.pop('Name')
        result[name] = elem
    data['Vessels'] = result