关于字典:访问像Python中的属性一样的OrderedDict键

Accessing OrderedDict keys like attributes in Python

我想写一个容器类

  • 允许索引(在字典样式中),例如data['a']和类似属性的访问,例如data.a;这在这里解决。
  • 保留添加条目的顺序,例如通过将collections.OrderedDict子类化;在此处解决
  • 我将解决方案修改为1。将collections.OrderedDict分为dict子类,但不起作用;见下文。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    包含故障注释的两行会导致以下错误:

    1
    2
    3
    4
    5
    6
    7
    8
        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'

    1
    2
    3
    4
        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'

    有解决办法吗?__init__功能是否可以修补?


    首先,如果你只是想让它正常工作,不知道你的尝试有什么问题,在Pypi、ActiveState等上有多个"attrdict"实现。搜索一个并查看它的代码,或者安装并使用它。

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

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

    当您使用dict时,至少在cpython中不会注意到这个问题,因为dict是在c中实现的,它需要操作的特殊成员存储在c结构中,而不是存储在__dict__中,因此您不能丢失它们。但是对于在Python中实现的任何需要任何特殊成员的类,问题都会变得明显。

    python有定制属性访问的特殊方法。您要做的是实现这些方法:

    1
    2
    3
    4
    5
    6
    7
    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]

    但是,在这种情况下,即使这样也不能解决所有问题,因为当OrderedDict中的代码试图使用__root时,仍然会调用重写!要解决这个问题,您只需要将这些方法中的一些调用转发到dict,而不是全部。

    "有些"很复杂,很难纠正。一个更简单的解决方案是封装并委托给OrderedDict,而不是继承:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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]

    或者,更简单地说,只需使用一个有序的dict作为你的__dict__,这是一个元类的范例,但是如果你想快速而肮脏地完成它,你可以在__init____new__中完成。

    后一种解决方案只是让您成为一个具有恰好是有序的属性的普通对象,而不是一个映射。但转发映射方法也很容易。执行__getitem____setitem____delitem____iter____len__转发self._od,由collections.MutableMapping继承填写其余API。

    现在,如果您返回查看pypi上的"attrdict"并单击实现,您将看到它们都是这样做的:它们将__getattr____getitem__转发到ordereddict的__getitem__,依此类推。