首页 > 解决方案 > 在配置文件中使用字典而不是列表是一个坏习惯吗?

问题描述

考虑以下 yaml 配置文件:

items:
    item_A:
      field_A: some_value
      field_B: some_other_value
    item_B:
      field_A: some_value
      field_B: some_other_value

表示这一点的合乎逻辑的方法是在每个项目前面添加破折号,使其成为项目列表:

items:
    - item_A:
        field_A: some_value
        field_B: some_other_value
    - item_B:
        field_A: some_value
        field_B: some_other_value

我想轻松访问对象的名称(item_A 等)。

在迭代以前的配置时,

for item in items:
    print(item) # here, 'item' will be the keys in the items dict (eg. 'item_A')

与后者相比

for item in items:
    print(item) # here, 'item' will be a full dict (eg. "item_A":{"field_A":"some_value"..)
    # if I want access to the name item_A, I need to do item.keys()[0]- not so friendly

我知道第二种表示在逻辑上是适合这种情况的一种,但我发现使用第一种表示更方便,以便能够迭代并直接获取键/对象名称。

所以,我的问题是:

在提供的示例中将列表表示为字典是否被认为是一种坏习惯,以便轻松访问项目的名称/键?

这样做有什么缺点或问题吗?

标签: pythonlistdictionaryconfigurationyaml

解决方案


什么是坏习惯,什么是合乎逻辑的,是有争议的。我认为您假设因为items您的根级别映射中的键是复数,所以该值由多个项目组成并且应该是一个序列。我认为这不一定是真的。

但是,如果映射中作为值的键值对items(即带有键item_A和的键item_B)的顺序确实比您必须使用列表更重要,您可能想要这样做:

items:
- name: item_A
  field_A: some_value
  field_B: some_other_value
- name: item_B
  field_A: some_value
  field_B: some_other_value

当您将其加载到变量data中时,不再item_B像您的解决方案那样轻松访问。加载后可以做的是:

data['items'] = Items(data['items'])

with 类通过提供和Items适当地提供对底层数据结构的访问,这样你就可以做到__getitem____iter__

items = data['items']
for item_name in items:
   item = items[item_name]

您可以使用标签在加载后无需后处理步骤来执行此操作

items: !Items
- name: item_A
  field_A: some_value_1
  field_B: some_other_value_1
- name: item_B
  field_A: some_value_2
  field_B: some_other_value_2

并注册您的课程Items。然而,这似乎不像没有标签的版本那样用户友好,尽管在这种情况下显式比隐式更好。

假设以上是input.yaml

import sys
from pathlib import Path
import ruamel.yaml

input = Path('input.yaml')

yaml = ruamel.yaml.YAML()

@yaml.register_class
class Items:
    def __init__(self, items):
        self.item_list = items

    @classmethod
    def from_yaml(cls, constructor, node):
        return cls(constructor.construct_sequence(node, deep=True))

    def __getitem__(self, key):
        if isinstance(key, int):
            return self.item_list[key]
        for item in self.item_list:
            if item['name'] == key:
                return item

    def __iter__(self):
        for item in self.item_list:
             yield item['name']

data = yaml.load(input)
items = data['items']
print('item1', items[1]['field_A'])
print('item2', items['item_A']['field_A'])
for item_name in items:
    item = items[item_name]
    print('item3', item['field_B'])

这使:

item1 some_value_2
item2 some_value_1
item3 some_other_value_1
item3 some_other_value_2

如果items是 YAML 文档根级别的唯一键,那么当然根本不需要该键,那么您应该将标签放在文档的开头并有一个序列作为根节点。


推荐阅读