2012-06-15 33 views
6

Tôi có một cấu hình YAML trông giống như:Làm thế nào để tải một tập tin pyYAML và truy cập nó bằng cách sử dụng các thuộc tính thay vì sử dụng ký pháp từ điển?

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

Tôi đang sử dụng mô-đun Python YAML để tải nó nhưng tôi muốn truy cập nó trong những cách tốt hơn như:

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

Những gì tôi muốn là để có thể làm: print(config.content).

+1

Bản sao của http://stackoverflow.com/questions/2352181/how-to-use-a-dot-to-access-members-of-dictionary – Justin

+0

@Justin: Đây không phải là bản sao của câu hỏi này, bởi vì bạn có thể chỉ cần vá bộ nạp YAML để tạo ra các đối tượng của bất kỳ lớp nào bạn mong muốn hơn là các trường hợp của 'dict'. –

Trả lời

5

Bạn có thể sử dụng ký hiệu đối tượng với bộ từ điển bằng cách sử dụng lớp sau, như đã thảo luận trong this câu trả lời:

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

Lớp này trong hành động:

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

Sửa này hoạt động một cách đệ quy với phụ từ điển, ví dụ:

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

Tôi nghĩ rằng điều này không làm việc đệ quy, như bạn có thể tưởng tượng cùng một vấn đề lặp đi lặp lại cho các mục khác trong cây cấu hình. – sorin

+0

Nếu tôi hiểu ý của bạn, điều này sẽ hoạt động đệ quy. Bản chỉnh sửa của tôi có trả lời điểm của bạn không? – Chris

+1

Tại sao điều này (câu trả lời đơn giản nhất ra khỏi đó) không upvoted một triệu lần? Có hàng chục câu hỏi tương tự/trùng lặp trả lời câu hỏi này theo cách phức tạp như vậy! – brandonscript

4

Cách dễ nhất để làm điều này có lẽ là ghi đè lên hàm tạo YAML cho tag:yaml.org,2002:map để nó trả về một lớp từ điển tùy chỉnh thay vì một từ điển thông thường.

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 

Lưu ý rằng điều này sẽ phá vỡ mọi thứ khác trong quá trình sử dụng YAML, nếu nó mong muốn mọi thứ hoạt động bình thường theo cách Python. Bạn có thể sử dụng một bộ tải tùy chỉnh, nhưng cá nhân tôi tìm thấy tài liệu PyYAML một chút mê cung, và có vẻ như các tác dụng phụ là toàn cầu và dễ lây lan như một quy tắc hơn là một ngoại lệ.

Bạn đã được cảnh báo.

Là một thay thế, nếu schema của bạn là tương đối tĩnh bạn có thể viết các lớp học của riêng bạn và deserialize để những người (ví dụ, class Config với idname tài sản). Nó có lẽ sẽ không có giá trị chi phí của mã phụ, tuy nhiên.

+0

Đơn giản hóa và cải thiện điều này thành https://gist.github.com/ktaragorn/9cf6d368378b0f65a3a0. Không có monkeypatching và nó hoạt động trong một thời trang lồng nhau cũng –

+0

@KarthikT: Không cho các từ điển lồng nhau bên trong mảng. –

+0

Tôi thậm chí không nghĩ rằng trường hợp sử dụng .. điều đó xảy ra thường xuyên khi bạn đang xem xét các tập tin cấu hình? –

Các vấn đề liên quan