我正在创建一组Python 2.7 / 3 +工具,以编程方式修改我的团队不断使用的XML文件的内容。到目前为止,我正在将XML解析为有意义的类对象属性(通过**kwargs
和.__dict__.update()
或setattr()
),我将其与一些相当复杂的方法相关联。我非常希望能够根据方法调用来定义自动化,例如: foo.do_complex_modification(x)
。
我现在正处于将修改后的数据写回XML文件的位置,供我们的其他软件使用。该文件的内容很好,但遗憾的是我们的遗留工具只接受保留订单的XML ,这是我不能指望提供的Python对象字典。我可以在解决XML时没有问题地以“正确”的顺序阅读并以某种方式存储,但是修改遗留系统不是一种选择。
可能相关,还有XML的XSD架构。
问题:序列化我的类属性的最pythonic或优雅方式是什么,以便保留原始顺序?如果合适的话,当我从对象的.sort(key=ordering_function)
读回来时,我会怎样写作__dict__
?
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之前的任何版本上都没有订购。
答案 0 :(得分:3)
最大的问题可能是因为你不断传递关键字参数,这不会保留它们所定义的顺序。另一个问题是class.__dict__
是无序的。这是一种解决这两件事的方法(不假设您希望按名称按字母顺序排列属性)。替换类'special __dict__
属性的技术是允许的,因为正如documentation所说,它可以是“字典或其他映射对象”(强调我的)。在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
的替代方法是创建自己的映射类。这是来自example的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()
函数,则会按添加顺序访问属性(这是在修改你的问题之前,你原本似乎只想要的东西。)
答案 1 :(得分:0)
即使您对类属性进行排序,只要它们存储在dict
中,订单就会发生变化。最好的方法是使用OrderedDict
来保留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)))