访问OrderedDict键,如Python中的属性

wap*_*p26 3 python dictionary

我想写一个容器类

  1. 允许索引(在字典样式中),例如data['a'],属性类访问,例如data.a; 这是在这里解决的
  2. 保留添加条目的顺序,例如通过子类化collections.OrderedDict; 这是在这里解决的

我将1.的解决方案改为子类collections.OrderedDict而不是dict它但不起作用; 见下文.

from collections import OrderedDict

class mydict(OrderedDict):
    def __init__(self, *args, **kwargs):
        super(mydict, self).__init__(*args, **kwargs)
        self.__dict__ = self

D = mydict(a=1, b=2)

#D['c'] = 3 # fails
D.d    = 4

#print D    # fails
Run Code Online (Sandbox Code Playgroud)

带有失败注释的两行会导致以下错误:

    print D
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/collections.py", line 176, in __repr__
    return '%s(%r)' % (self.__class__.__name__, self.items())
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/collections.py", line 113, in items
    return [(key, self[key]) for key in self]
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/collections.py", line 76, in __iter__
    root = self.__root
AttributeError: 'struct' object has no attribute '_OrderedDict__root'
Run Code Online (Sandbox Code Playgroud)

    D['c'] = 3
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/collections.py", line 58, in __setitem__
    root = self.__root
AttributeError: 'mydict' object has no attribute '_OrderedDict__root'
Run Code Online (Sandbox Code Playgroud)

这个问题有方法解决吗 ?可以将__init__功能进行修补?

aba*_*ert 6

首先,如果你只想让这个工作,不明白你的尝试有什么问题,在PyPI,ActiveState等上有多个"AttrDict"实现.搜索一个并查看它的代码 - 或者只是安装它并使用它.


self.__dict__ = self不是你想要的.我意识到人们推荐它,但它有一些基本的主要问题.其中一个问题正是你所看到的问题.

特别是,它意味着您现在已经丢失了所有现有属性,包括OrderedDict自身使用的内部属性(就像__root您遇到的错误一样)和附加到所有对象的特殊属性(如__class__).

当你刚刚使用时dict你没有注意到这个问题 - 至少在CPython中 - 因为它dict是用C实现的,它需要操作的特殊成员存储在C结构中,而不是存储在C结构中__dict__,所以你不能失去他们.但是对于在Python中实现的任何需要任何特殊成员的类,问题变得明显.


Python有自定义属性访问的特殊方法.你想要做的是实现这些方法:

class AttrDict(OrderedDict):
    def __getattr__(self, name):
        return self[name]
    def __setattr__(self, name, value):
        self[name] = value
    def __delattr__(self, name):
        del self[name]
Run Code Online (Sandbox Code Playgroud)

但是,在这种情况下,即使这样也不适用于所有内容,因为当代码OrderedDict尝试使用时,您仍然会调用覆盖__root!要解决这个问题,你只需要将这些方法中的一些调用转发给你的dict,而不是全部.

而"某些"很复杂,很难做到.一个更简单的解决方案是封装和委托给一个OrderedDict而不是继承:

class AttrDict(object):
    def __init__(self, *args, **kwargs):
        self._od = OrderedDict(*args, **kwargs)
    def __getattr__(self, name):
        return self._od[name]
    def __setattr__(self, name, value):
        if name == '_od':
            self.__dict__['_od'] = value
        else:
            self._od[name] = value
    def __delattr__(self, name):
        del self._od[name]
Run Code Online (Sandbox Code Playgroud)

或者,更简单地说,只需使用OrderedDict作为你__dict__的元类的范例,但如果你想快速和肮脏,你可以在__init____new__代替.


后一种解决方案只会使您成为普通对象,其中的属性恰好是有序的,而不是映射.但是转发映射方法也很容易.实行__getitem__,__setitem__,__delitem__,__iter__,并__len__转发到self._od,并继承collections.MutableMapping于API的其余部分继续进行.

现在,如果你回去查查"AttrDict" PyPI上并点击实现,你会看到,这正是他们都是这么干的:他们都转发__getattr____getitem__给OrderedDict的__getitem__,等等.