可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have two classes in my code. first
is the parent, which second
inherits.
class first(object):
def __init(self,**kwargs):
pass
def __setattr__(self,name,value):
self.__dict__[name] = value
class second(first):
def do_something(self):
self.a = 1
self.b = 2
self.c = 3
when I am printing the class second (by e.g. second.__dict__
) I get the unordered dictionary. This is obvious. I want to change this behavior to get an ordered dictionary using the OrderedDict
class, but it does not work. I am changing implementation of first
in the following way:
class first(OrderedDict):
def __init__(self,**kwargs):
super(first,self).__init__(**kwargs)
def __setattr__(self,name_value):
super(first,self).__setattr__(name_value)
I would like to print second
using __dict__
or __repr__
, but I got the unordered dictionary. What should I change?
回答1:
You can simply redirect all attribute access to an OrderedDict
:
class first(object):
def __init__(self, *args, **kwargs):
self._attrs = OrderedDict(*args, **kwargs)
def __getattr__(self, name):
try:
return self._attrs[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
if name == '_attrs':
return super(first, self).__setattr__(name, value)
self._attrs[name] = value
Demo:
>>> from collections import OrderedDict
>>> class first(object):
... def __init__(self, *args, **kwargs):
... self._attrs = OrderedDict(*args, **kwargs)
... def __getattr__(self, name):
... try:
... return self._attrs[name]
... except KeyError:
... raise AttributeError(name)
... def __setattr__(self, name, value):
... if name == '_attrs':
... return super(first, self).__setattr__(name, value)
... self._attrs[name] = value
...
>>> class second(first):
... def do_something(self):
... self.a = 1
... self.b = 2
... self.c = 3
...
>>> s = second()
>>> s.do_something()
>>> s._attrs
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
You can't otherwise replace the __dict__
attribute with an OrderedDict
instance, because Python optimises instance attribute access by using the concrete class API to access the dictionary internals in C, bypassing the OrderedDict.__setitem__
hook altogether (see issue #1475692).
回答2:
I think the solutions in this thread focus too much on using OrderedDict
as if it is a necessity. The class already has a builtin __dict__
method, the only problem is ordering the keys. Here is how I am retrieving (key, value)
pairs from my class in the order they are entered:
class MyClass:
def __init__(self, arg1, arg2, arg3):
self._keys = []
self.arg1 = arg1
self.arg2 = arg2
self.arg3 = arg3
def __setattr__(self, key, value):
# store new attribute (key, value) pairs in builtin __dict__
self.__dict__[key] = value
# store the keys in self._keys in the order that they are initialized
# do not store '_keys' itelf and don't enter any key more than once
if key not in ['_keys'] + self._keys:
self._keys.append(key)
def items(self):
# retrieve (key, value) pairs in the order they were initialized using _keys
return [(k, self.__dict__[k]) for k in self._keys]
>>> x = MyClass('apple', 'orange', 'banana')
>>> print x.items()
[('arg1', 'apple'), ('arg2', 'orange'), ('arg3', 'banana')]
>>> x.arg1 = 'pear'
>>> print x.items()
[('arg1', 'pear'), ('arg2', 'orange'), ('arg3', 'banana')]
I'm using a class to store about 70 variables used to configure and run a much bigger program. I save a text copy of the initial (key, value)
pairs that can be used to initialize new instances of the class. I also save a text copy of the (key, value)
pairs after running the program because several of them are set or altered during the program run. Having the (key, value)
pairs in order simply improves the readability of the text file when I want to scan through the results.
回答3:
You can try by actually replacing __dict__ with OrderedDict:
from collections import OrderedDict
class Test(object):
def __init__(self):
self.__dict__ = OrderedDict()
self.__dict__['a'] = 0
self.__dict__['b'] = 1
self.__dict__['c'] = 2
test = Test()
print test.__dict__
test.a, test.b, test.c = 'a', 'b', 'c'
print test.__dict__
This should printout:
OrderedDict([('a', 0), ('b', 1), ('c', 2)])
OrderedDict([('a', 'a'), ('b', 'b'), ('c', 'c')])
回答4:
Another option; you can also manipulate new if you wish.
from collections import OrderedDict
class OrderedClassMeta(type):
@classmethod
def __prepare__(cls, name, bases, **kwds):
return OrderedDict()
class OrderedClass(metaclass=OrderedClassMeta):
pass
class A(OrderedClass):
def __init__(self):
self.b=1
self.a=2
def do(self):
print('do')
class B(OrderedClass):
def __init__(self):
self.a=1
self.b=2
def do(self):
print('do')
a=A()
print(a.__dict__)
b=B()
print(b.__dict__)