[英]What is the most pythonic way to re-order a class objects' attributes?
我正在創建一組Python 2.7 / 3 +工具,以編程方式修改我的團隊不斷使用的XML文件的內容。 到目前為止,我正在將XML解析為有意義的類對象屬性(通過**kwargs
和.__dict__.update()
或setattr()
),我將其與一些相當復雜的方法相關聯。 我真的很想能夠根據方法調用來定義自動化,例如foo.do_complex_modification(x)
。
我現在正處於將修改后的數據寫回XML文件的位置,供我們的其他軟件使用。 該文件的內容很好,但遺憾的是我們的遺留工具只接受保留順序的XML,我不能指望提供的Python對象字典。 我可以在解決XML時沒有問題地以“正確”順序閱讀並以某種方式存儲,但是修改遺留系統不是一種選擇。
可能相關,還有XML的XSD架構。
問題 :序列化我的類屬性以保持原始順序的最pythonic或優雅方法是什么? 如果合適的話,當從對象的__dict__
讀回時,我將如何寫,例如.sort(key=ordering_function)
?
class Foo(object):
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def really_complex_method(self):
pass
def attrs(cls):
return [(k, v) for k, v in cls.__dict__.items() if k[:1] != '_']
d = dict(bar=1, baz=2, quux=3) # Need them back in this particular order
print(attrs(Foo(**d)))
返回
[('quux', 3), ('bar', 1), ('baz', 2)]
並且在Python 3.6之前的任何版本上都沒有排序。
最大的問題可能是因為你不斷傳遞關鍵字參數,這不會保留它們被定義的順序。 另一個問題是class.__dict__
是無序的。 這是一種解決這兩件事的方法(不假設您希望按名稱按字母順序排列屬性)。 替換類'special __dict__
屬性的技術是允許的,因為正如文檔所說,它可以是“字典或其他映射對象 ”(強調我的)。 在Python 2和3中都是如此。
from collections import OrderedDict
from operator import itemgetter
class Foo(object):
def __init__(self, *keyvalues):
self.__dict__ = OrderedDict(keyvalues)
def really_complex_method(self):
pass
def attrs(instance):
""" Return a list of instance attributes sorted by their value. """
return sorted(instance.__dict__.items(), key=itemgetter(1))
print(attrs(Foo(('bar', 1), ('baz', 2), ('quux', 3), ('question', -42))))
輸出:
[('question', -42), ('bar', 1), ('baz', 2), ('quux', 3)]
使用OrderedDict
的另一種方法是創建自己的映射類。 這是來自PEP 3115的示例 takien(關於Python 3中的元類,但主題不相關)。 它也適用於Python 2和3):
from operator import itemgetter
class MemberTable(dict):
""" Custom dictionary that keeps track of the order members (keys) are added. """
def __init__(self, *args, **kwargs):
super(MemberTable, self).__init__(*args, **kwargs)
self.member_names = []
def __setitem__(self, key, value):
# if the key is not already defined, add to the list of keys.
if key not in self:
self.member_names.append(key)
super(MemberTable, self).__setitem__(self, key, value)
class Foo(object):
def __init__(self, *keyvalues):
self.__dict__ = MemberTable(keyvalues)
def really_complex_method(self):
pass
def attrs(instance):
""" Return a list of instance attributes sorted their value. """
return sorted(instance.__dict__.items(), key=itemgetter(1))
print(attrs(Foo(('bar', 1), ('baz', 2), ('quux', 3), ('question', -42))))
另一種方法是,不需要更改實例的__dict__
,就是讓類跟蹤添加到它的順序屬性,然后按順序迭代它們:
from operator import itemgetter
class Foo(object):
def __init__(self, *keyvalues):
self._sequence = []
for (key, value) in keyvalues:
setattr(self, key, value)
self._sequence.append(key) # keep track of order added
def __iter__(self):
for key in self._sequence:
yield key, getattr(self, key)
def really_complex_method(self):
pass
def attrs(instance):
""" Return a list of instance attributes sorted their value. """
return sorted((item for item in instance), key=itemgetter(1))
print(attrs(Foo(('bar', 1), ('baz', 2), ('quux', 3), ('question', -42))))
請注意 ,在所有這些實現的,如果sorted()
沒有使用attrs()
函數的屬性將在(這是你最初似乎修改你的問題之前,想的唯一的事)加入他們的順序進行訪問。
即使您對類屬性進行排序,只要它們存儲在dict
,順序就會發生變化。 最好的方法是使用OrderedDict
,它將保留sorted()
方法的sorted()
。
from collections import OrderedDict
class Foo(object):
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def really_complex_method(self):
pass
def attrs(cls):
return OrderedDict(sorted(cls.__dict__.items()))
d = dict(bar=1, baz=2, quux=3)
print(attrs(Foo(**d)))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.