2012-06-15 11 views
6

Mam config YAML, który wygląda tak:Jak załadować plik pyYAML i uzyskać do niego dostęp za pomocą atrybutów zamiast używać notacji słownika?

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

używam moduł Pythona YAML aby go załadować, ale chcę do niego dostęp w lepszych sposobów, takich jak:

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

Co chcę jest być w stanie wykonać: print(config.content).

+1

Duplikat http://stackoverflow.com/questions/2352181/how-to-use-a-dot-to-access-members-of-dictionary – Justin

+0

@Justin: To nie jest duplikat tego pytania, ponieważ możesz po prostu załatać program ładujący YAML, aby tworzyć obiekty dowolnej klasy, a nie instancji 'dyktowania'. –

Odpowiedz

5

Można używać notacji obiektu ze słowników, stosując następujące klasy, jak to omówiono w this odpowiedź:

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

Ta klasa w akcji:

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

Edycja To działa rekursywnie z pod- słowniki, na przykład:

>>> 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 
+0

Myślę, że to nie działa rekursywnie, ponieważ możesz sobie wyobrazić, że ten sam problem powtarza się dla innych wpisów w drzewie konfiguracji. – sorin

+0

Jeśli rozumiem, co masz na myśli, to działa rekursywnie. Czy moja edycja odpowiada na twój punkt widzenia? – Chris

+1

Dlaczego to (najprostsza odpowiedź tam) nie jest upvoted milion razy? Istnieją dziesiątki podobnych/zduplikowanych pytań, które odpowiedzą na to w tak skomplikowany sposób! – brandonscript

4

Najprostszym sposobem na to jest prawdopodobnie nadpisanie konstruktora YAML dla tag:yaml.org,2002:map, więc zwraca on niestandardową klasę słownika zamiast zwykłego słownika.

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 

Zauważ, że to złamie wszystkiego innego w procesie, który używa YAML, jeśli spodziewa się wszystko do pracy normalnej Pythona sposób. Możesz użyć niestandardowego programu ładującego, ale osobiście uważam dokumentację PyYAML za nieco labirynt i wydaje się, że skutki uboczne są globalne i zakaźne z reguły, a nie jako wyjątek.

Zostałeś ostrzeżony.

Jako alternatywę jeśli schemat jest względnie statyczne można napisać własne klasy i deserializowania osobom (np class Config z id i name właściwości). Prawdopodobnie nie byłby jednak wart kosztu dodatkowego kodu.

+0

Uproszczone i ulepszone do https://gist.github.com/ktaragorn/9cf6d368378b0f65a3a0. Nie ma monkeypatchingu i działa on również w trybie zagnieżdżonym. –

+0

@KarthikT: Brakuje słowników zagnieżdżonych w tablicach. –

+0

Nawet nie uważałem, że przypadek użycia. Czy zdarza się to często, gdy rozważasz pliki konfiguracyjne? –