2010-11-08 35 views
11

Tôi có chức năng sau đây thực hiện công việc thô để phân tích cú pháp tệp XML thành từ điển.Làm thế nào chức năng này có thể được viết lại để thực hiện OrderedDict?

Thật không may, vì từ điển Python không được đặt hàng, tôi không thể chuyển qua các nút như tôi muốn.

Làm cách nào để thay đổi điều này để nó xuất ra từ điển được đặt hàng phản ánh thứ tự ban đầu của các nút khi được lặp với 'for'.

def simplexml_load_file(file): 
    import collections 
    from lxml import etree 

    tree = etree.parse(file) 
    root = tree.getroot() 

    def xml_to_item(el): 
     item = None 
     if el.text: 
      item = el.text 
     child_dicts = collections.defaultdict(list) 
     for child in el.getchildren(): 
      child_dicts[child.tag].append(xml_to_item(child)) 
     return dict(child_dicts) or item 

    def xml_to_dict(el): 
     return {el.tag: xml_to_item(el)} 

    return xml_to_dict(root) 

x = simplexml_load_file('routines/test.xml') 

print x 

for y in x['root']: 
    print y 

Đầu ra:

{'root': { 
    'a': ['1'], 
    'aa': [{'b': [{'c': ['2']}, '2']}], 
    'aaaa': [{'bb': ['4']}], 
    'aaa': ['3'], 
    'aaaaa': ['5'] 
}} 

a 
aa 
aaaa 
aaa 
aaaaa 

Làm thế nào tôi có thể thực hiện collections.OrderedDict vì vậy mà tôi có thể chắc chắn nhận được theo đúng thứ tự của các nút?

tập tin XML để tham khảo:

<root> 
    <a>1</a> 
    <aa> 
     <b> 
      <c>2</c> 
     </b> 
     <b>2</b> 
    </aa> 
    <aaa>3</aaa> 
    <aaaa> 
     <bb>4</bb> 
    </aaaa> 
    <aaaaa>5</aaaaa> 
</root> 
+0

bản sao của http: // stackoverflow. com/questions/4123266/python-looping-dường như không theo dõi bởi cùng một tác giả. –

Trả lời

27

Bạn có thể sử dụng lớp con mới OrderedDictdict được thêm vào mô-đun collections của thư viện chuẩn trong phiên bản 2.7 *. Trên thực tế những gì bạn cần là một sự kết hợp Ordered + defaultdict mà không tồn tại-nhưng nó có thể tạo ra một bằng subclassing OrderedDict như minh họa dưới đây:

import collections 

class OrderedDefaultdict(collections.OrderedDict): 
    """ A defaultdict with OrderedDict as its base class. """ 

    def __init__(self, default_factory=None, *args, **kwargs): 
     if not (default_factory is None 
       or isinstance(default_factory, collections.Callable)): 
      raise TypeError('first argument must be callable or None') 
     super(OrderedDefaultdict, self).__init__(*args, **kwargs) 
     self.default_factory = default_factory # called by __missing__() 

    def __missing__(self, key): 
     if self.default_factory is None: 
      raise KeyError(key,) 
     self[key] = value = self.default_factory() 
     return value 

    def __reduce__(self): # optional, for pickle support 
     args = (self.default_factory,) if self.default_factory else tuple() 
     return self.__class__, args, None, None, self.iteritems() 

    def __repr__(self): # optional 
     return '%s(%r, %r)' % (self.__class__.__name__, self.default_factory, 
           list(self.iteritems())) 

def simplexml_load_file(file): 
    from lxml import etree 

    tree = etree.parse(file) 
    root = tree.getroot() 

    def xml_to_item(el): 
     item = el.text or None 
     child_dicts = OrderedDefaultdict(list) 
     for child in el.getchildren(): 
      child_dicts[child.tag].append(xml_to_item(child)) 
     return collections.OrderedDict(child_dicts) or item 

    def xml_to_dict(el): 
     return {el.tag: xml_to_item(el)} 

    return xml_to_dict(root) 

x = simplexml_load_file('routines/test.xml') 
print(x) 

for y in x['root']: 
    print(y) 

Sản lượng sản xuất từ ​​tập tin XML thử nghiệm của bạn trông như thế này:

Đầu ra:

{'root': 
    OrderedDict(
     [('a', ['1']), 
     ('aa', [OrderedDict([('b', [OrderedDict([('c', ['2'])]), '2'])])]), 
     ('aaa', ['3']), 
     ('aaaa', [OrderedDict([('bb', ['4'])])]), 
     ('aaaaa', ['5']) 
     ] 
    ) 
} 

a 
aa 
aaa 
aaaa 
aaaaa 

Điều tôi nghĩ là gần với những gì bạn muốn.

* Nếu phiên bản Python của bạn không có OrderedDict, được giới thiệu trong phiên bản v2.5, bạn có thể sử dụng công thức ActiveState của Raymond Hettinger làm lớp cơ sở thay thế.

cập nhật nhỏ:

Added một phương pháp __reduce__() mà sẽ cho phép các thể hiện của lớp được ngâm và unpickled đúng cách. Điều này là không cần thiết cho câu hỏi này, nhưng đã đưa ra trong similar một.

1

Có rất nhiều khả năng thực hiện OrderedDict được liệt kê trong câu trả lời ở đây: How do you retrieve items from a dictionary in the order that they're inserted?

Bạn có thể tạo mô-đun OrderedDict riêng của bạn để sử dụng trong mã của riêng bạn bằng cách sao chép một của việc triển khai. Tôi cho rằng bạn không có quyền truy cập vào OrderedDict vì phiên bản Python bạn đang chạy.

Một khía cạnh thú vị của câu hỏi là nhu cầu có thể có đối với chức năng mặc định. Nếu bạn cần điều này, bạn có thể thực hiện phương thức __missing__ để có được hiệu ứng mong muốn.

1

Công thức từ martineau có tác dụng đối với tôi, nhưng nó có vấn đề với phương thức copy() kế thừa từ DefaultDict.Các phương pháp sau đây khắc phục nhược điểm này:

class OrderedDefaultDict(OrderedDict): 
    #Implementation as suggested by martineau 

    def copy(self): 
     return type(self)(self.default_factory, self) 

Vui lòng xem xét, rằng việc thực hiện điều này không deepcopy, mà dường như đặc biệt đối với các từ điển mặc định chứ không phải là điều phải làm trong hầu hết các tình huống

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