使用点表示法和链接的Python dict

2024-04-28 23:47:10 发布

您现在位置:Python中文网/ 问答频道 /正文

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

  • 能够设置和获取值的点表示法
  • 键值功能,如dict(即setitem、getitem)
  • Can链点标记操作

目标是如果我有类似example = DotDict()的东西,我可以对它做以下操作example.configuration.first= 'first',它将在example下实例化适当的DotDict实例,真正痛苦的警告是,如果操作不是赋值的,它应该像dict那样简单地引发一个KeyError

这是我天真地组装的东西

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

编辑:

这是我在用户1320237的推荐下提出的解决方案。

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

在实际的实现中还有一点需要改进,但是它做得很好。它的工作方式是检查堆栈中的最后一个帧,并检查存储属性操作的字节码,这意味着正在执行的操作具有a.b.this.doesnt.exist.yet = 'something'说服力。我很好奇这是否可以在CPython之外的其他口译员身上完成。


Tags: keyimportselfreturnifvalue堆栈example
3条回答

这是另一个解决方案: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
  }
}
>>>

根据用户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)

您可能需要覆盖这些边缘情况的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        

相关问题 更多 >