9

次のような YAML 構成があります。

config:
 - id: foo
 - name: bar
content:
 - run: xxx
 - remove: yyy

Python YAML モジュールを使用してロードしていますが、次のようなより良い方法でアクセスしたいと考えています。

stream = open(filename)
config = load(stream, Loader=Loader)
print(config['content'])

私がしたいことは、できるようになることです: print(config.content).

4

2 に答える 2

11

この回答で説明されているように、次のクラスを使用して辞書でオブジェクト表記を使用できます。

class DictAsMember(dict):
    def __getattr__(self, name):
        value = self[name]
        if isinstance(value, dict):
            value = DictAsMember(value)
        return value

このクラスの動作:

>>> my_dict = DictAsMember(one=1, two=2)
>>> my_dict
{'two': 2, 'one': 1}
>>> my_dict.two
2

編集これは、サブ辞書で再帰的に機能します。次に例を示します。

>>> my_dict = DictAsMember(one=1, two=2, subdict=dict(three=3, four=4))
>>> my_dict.one
1
>>> my_dict.subdict
{'four': 4, 'three': 3}
>>> my_dict.subdict.four
4
于 2012-06-15T10:52:41.223 に答える
4

これを行う最も簡単な方法は、おそらく YAML コンストラクターを上書きしてtag:yaml.org,2002:map、通常の辞書ではなくカスタム辞書クラスを返すことです。

import yaml

class AttrDict(object):
    def __init__(self, attr):
        self._attr = attr
    def __getattr__(self, attr):
        try:
            return self._attr[attr]
        except KeyError:
            raise AttributeError

def construct_map(self, node):
    # WARNING: This is copy/pasted without understanding!
    d = {}
    yield AttrDict(d)
    d.update(self.construct_mapping(node))

# WARNING: We are monkey patching PyYAML, and this will affect other clients!    
yaml.add_constructor('tag:yaml.org,2002:map', construct_map)

YAML = """
config:
  - id: foo
  - name: bar
content:
  - run: xxx
  - remove: yyy
"""

obj = yaml.load(YAML)

print(obj.config[0].id) # prints foo

これにより、すべてが通常の Python の方法で機能することが期待される場合、YAML を使用するプロセスの他のすべてが壊れることに注意してください。カスタム ローダーを使用することもできますが、個人的には PyYAML のドキュメントは少し複雑で、副作用はグローバルであり、例外ではなく原則として伝染性があるようです。

あなたは警告されました。

別の方法として、スキーマが比較的静的な場合は、独自のクラスを作成し、それらに逆シリアル化することができます (たとえば、class Configおよびidプロパティnameを使用)。ただし、余分なコードのコストに見合う価値はないでしょう。

于 2012-06-15T11:19:12.203 に答える