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