用OrderedDict替换对象的默认__dict__

问题描述 投票:4回答:4

我的代码中有两个类。 firstsecond继承的父母。

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

当我打印第二课时(例如second.__dict__)我得到无序字典。这很明显。我想更改此行为以使用OrderedDict类获取有序字典,但它不起作用。我正在通过以下方式更改first的实现:

class first(OrderedDict):   
    def __init__(self,**kwargs):  
        super(first,self).__init__(**kwargs)  
    def __setattr__(self,name_value):  
        super(first,self).__setattr__(name_value)  

我想用second__dict__打印__repr__,但我得到了无序字典。我应该改变什么?

python dictionary ordereddictionary
4个回答
8
投票

您可以简单地将所有属性访问重定向到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

演示:

>>> 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)])

你不能用__dict__实例替换OrderedDict属性,因为Python通过使用具体的类API来访问C中的字典内部来优化实例属性访问,完全绕过OrderedDict.__setitem__钩子(参见issue #1475692)。


4
投票

我认为这个线程中的解决方案过于关注使用OrderedDict,就好像它是必需的一样。该类已经有一个内置的__dict__方法,唯一的问题是订购密钥。以下是我按照输入顺序从班级中检索(key, value)对的方法:

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')]

我正在使用一个类来存储大约70个用于配置和运行更大程序的变量。我保存了初始(key, value)对的文本副本,可用于初始化类的新实例。我还在运行程序后保存了(key, value)对的文本副本,因为在程序运行期间设置或更改了其中几个。当我想要扫描结果时,按顺序使用(key, value)对可以简化文本文件的可读性。


1
投票

你可以尝试用OrderedDict实际替换__dict__:

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__

这应该打印输出:

OrderedDict([('a', 0), ('b', 1), ('c', 2)])
OrderedDict([('a', 'a'), ('b', 'b'), ('c', 'c')])

-1
投票

另外一个选项;如果你愿意,你也可以操纵新的。

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__)
© www.soinside.com 2019 - 2024. All rights reserved.