3

理想情况下,我的目标是dict在 Python 中扩展(或非常相似) a 并具有附加功能的类:

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

目标是,如果我有类似的事情example = DotDict()可以针对它执行以下操作,example.configuration.first= 'first'并且它将实例化适当的 DotDict 实例example,但真正痛苦的警告是,如果操作不是赋值,它应该简单地引发 a KeyErrorlike a dictwould do

这是我天真的组装的

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它的第一个操作是 agetattr(a,b)而不是 asetattr所以我无法轻易确定操作堆栈中的最后一个操作是否是一个赋值.

我想知道的是是否有一种好方法可以确定操作堆栈中的最后一个操作,或者至少它是否是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 之外的其他解释器上完成。

4

5 回答 5

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        
于 2012-05-03T20:49:42.610 回答
1

根据user1320237的回答,这就是我得到的。它似乎做你想做的事。

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)
于 2012-05-03T21:15:53.133 回答
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
  }
}
>>>
于 2013-08-21T07:22:24.820 回答
0

这是使用包装器的替代解决方案。

于 2012-10-19T08:13:12.750 回答
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
于 2016-11-16T06:17:47.297 回答