簡體   English   中英

重新排序類對象屬性的最pythonic方法是什么?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM