【问题标题】:Replacing default __dict__ for object with OrderedDict用 OrderedDict 替换对象的默认 __dict__
【发布时间】:2015-01-14 11:13:43
【问题描述】:

我的代码中有两个类。 first 是父级,second 继承。

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)  

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

【问题讨论】:

  • __dict__OrderDict 实例本身并不是 OrderedDict 实例...
  • 你能解释一下为什么你想要这个吗?您可能还可以探索其他方法。
  • 您为什么需要__dict__ 来订购?普通的dicts 效率更高,对于打印,您只需在__repr____str__ 方法中对dict 进行排序。
  • 我希望元素的顺序与创建它们时相同。我在屏幕上打印这些信息并订购事宜。在这种情况下排序没有帮助。
  • @martineau 公平点。当然,这对于 Python 3.6+ 来说不是问题,因为普通的 dict 现在保持插入顺序。

标签: python dictionary ordereddictionary


【解决方案1】:

我认为这个线程中的解决方案过于关注使用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) 对可以提高文本文件的可读性。

【讨论】:

  • 这种方法也适用于没有内置 collections.OrderedDict 的 Python 2.7 之前的版本。存储新值不会很快,因为现在这样做需要线性搜索......另一方面,读取现有属性的值将像通常那样快速(即不强制所有访问通过__getattr__()__setattr__() 来电。
【解决方案2】:

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

【讨论】:

  • 非常感谢您的演示。事实上,它在这个简单的例子中是有效的,但是在我的怪异代码中实现它之后,我得到了一个错误:RuntimeError: maximum recursion depth exceeded。我正在寻找代码中的错误,但与此同时,也许你能找到这个问题的原因?或提出替代解决方案以避免此类问题。我不想增加递归深度。
  • @Przemo:您的__setattr__ 可能是在自称?您在这里如何使用super()?确保不要在错误的对象中设置_attrs
  • @Przemo:增加递归深度并不能解决这个问题,你有一个 infinite 循环。
  • @Suki:另外使用*args怎么样?
  • @martineau:@martineau:OrderedDictdict 的子类,而普通的__getattribute____setattr__ 实例实现(在C 中)刚刚进入C 级别数据结构。不参考OrderedDict 类的自定义__setitem__ 方法。当您实际打印__dict__ 引用时,您将看到一个空的OrderedDict 实例,因为没有维护所有内部订单记录结构。 list(d.__dict__) 是空的,list(d.__dict__.items()) 也是空的,只有 len(d.__dict__) 是正确的。但密钥已存储。
【解决方案3】:

另一种选择;如果你愿意,你也可以操纵new

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

【讨论】:

  • 请在发帖前尝试您的答案,它不起作用。 __prepare__ 的返回值被复制到 C 中的标准字典中。参见 mail.python.org/pipermail/python-list/2012-January/618121.html
  • 原始帖子存在缩进问题(对于“def prepare”)。如果您清理缩进,并使用 Python 3.6,结果如下所示: >>> a=A() >>> print(a.__dict__) {'b': 1, 'a': 2} >>> b=B() >>> print(b.__dict__) {'a': 1, 'b': 2} >>>
【解决方案4】:

您可以尝试将 __dict__ 实际替换为 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__

这应该打印出来:

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

【讨论】:

  • 这看起来是个坏主意。我不知道到底发生了什么,但我认为OrderedDict 最终会出现在真正的__dict__'__dict__' 插槽中,并且各种不可预测的废话正在发生。在没有self.__dict__['a'] = 0 行的情况下再试一次,看看。
  • @mdaoust:用另一个映射对象替换实例的__dict__ 很好。请参阅documentation
  • @mdaoust:抱歉,我之前的评论说做类似的事情很好是不正确。更多详细信息,请参见 Martijn Pieters'answer(及其下的 cmets)的结尾。
猜你喜欢
  • 2021-09-13
  • 1970-01-01
  • 2011-12-06
  • 2017-02-12
  • 2021-06-11
  • 2016-12-13
  • 1970-01-01
  • 1970-01-01
  • 2018-04-04
相关资源
最近更新 更多