Python dict使用点符号和链接

时间:2012-05-03 20:39:37

标签: python dictionary syntax

理想情况下,我希望实现的是一个扩展(或非常类似)Python中dict的类,具有附加功能:

  • 能够设置和获取值的点符号
  • 键值功能,例如dict(即setitem,getitem)
  • 可以连接点标记操作

我的目标是,如果我有类似example = DotDict()的内容,我可以针对它example.configuration.first= 'first'执行以下操作,它会在example下实例化相应的DotDict实例,其中非常痛苦的警告是该操作不是分配,它应该只是像KeyError那样提出dict

这是我天真的组装

class DotDict(dict):
    def __getattr__(self, key):
        """ Make attempts to lookup by nonexistent attributes also attempt key lookups. """
        import traceback
        import re
        s= ''.join(traceback.format_stack(sys._getframe(1),1))
        if re.match(r'  File.*\n.*[a-zA-Z]+\w*\.[a-zA-Z]+[a-zA-Z0-9_. ]*\s*=\s*[a-zA-Z0-9_.\'"]+',s):
            self[key] = DotDict()
            return self[key]

        return self[key]

    def __setattr__(self, key, value):
        if isinstance(value,dict):
            self[key] = DotDict(value)
        self[key] = value

除了一些常见的边缘情况之外,它必须说我绝对讨厌这种方法,必须有更好的方法。查看堆栈并在最后一行上运行正则表达式不是实现此目的的好方法。

问题的核心是Python从左到右解释代码行,所以当它到达a.b.c = 3之类的语句时,它的第一个操作是getattr(a,b)而不是setattr所以我无法轻易确定操作堆栈中的最后一个操作是否为赋值。

我想知道的是,如果有一种很好的方法来确定操作堆栈中的最后一个操作,或者至少它是setattr

编辑:

这是我提出的解决方案,这要归功于user1320237的推荐。

class DotDict(dict):
    def __getattr__(self, key):
        """ Make attempts to lookup by nonexistent attributes also attempt key lookups. """
        if self.has_key(key):
            return self[key]
        import sys
        import dis
        frame = sys._getframe(1)
        if '\x00%c' % dis.opmap['STORE_ATTR'] in frame.f_code.co_code:
            self[key] = DotDict()
            return self[key]

        raise AttributeError('Problem here')

    def __setattr__(self, key, value):
        if isinstance(value,dict):
            self[key] = DotDict(value)
        self[key] = value

在实际实施中还有一点点,但它做得非常棒。它的工作方式是检查堆栈中的最后一帧并检查STORE_ATTR操作的字节代码,这意味着正在执行的操作具有a.b.this.doesnt.exist.yet = 'something'说服力。如果可以在CPython之外的其他解释器上完成,我会很好奇。

5 个答案:

答案 0 :(得分:3)

您可能需要为这些边缘情况覆盖 getattribute ,然后使用

object.__getattribute__

查看模块dis。 但是你写的东西比拆解更好。

>>> import dis
>>> def g():
    a.b.c = 4


>>> dis.dis(g)
  2           0 LOAD_CONST               1 (4)
              3 LOAD_GLOBAL              0 (a)
              6 LOAD_ATTR                1 (b)
              9 STORE_ATTR               2 (c)
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE        

答案 1 :(得分:1)

根据用户1320237的回答,这是我得到的。它似乎做你想要的。

class A(object):
    def __getattribute__(self, attr):
        try:
            return super(A, self).__getattribute__(attr)
        except AttributeError:
            self.__setattr__(attr, A())
            return super(A, self).__getattribute__(attr)

答案 2 :(得分:1)

这是另一种解决方案:http://tech.zarmory.com/2013/08/python-putting-dot-in-dict.html

>>> d = DefaultDotDict({1: {2: 3}})
>>> d.a.b.c.d = "magic!"
>>> import json; print json.dumps(d, indent=2)
{
  "a": {
    "b": {
      "c": {
        "d": "magic!"
      }
    }
  }, 
  "1": {
    "2": 3
  }
}
>>>

答案 3 :(得分:0)

答案 4 :(得分:0)

class DotDict(dict):

"""Dot natation for dict"""

def __init__(self, theDict):
    super(MyDict, self).__init__()
    for key, item in theDict.items():
        if isinstance(item, dict):
            item = MyDict(item)
        self.__dict__[key] = item

def __getattr__(self, key):
    return self.__dict__[key]

def __setattr__(self, name, value):
    self.__dict__[name] = value