【问题标题】:Python dict using dot notation and chaining使用点符号和链接的 Python dict
【发布时间】:2012-05-13 10:00:57
【问题描述】:

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

  • 能够设置和获取值的点表示法
  • dict 等键值对功能(即 setitem、getitem)
  • 可以链接点表示的操作

目标是,如果我有类似 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

编辑:

这是我在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 之外的其他解释器上完成。

【问题讨论】:

标签: python dictionary syntax


【解决方案1】:

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

【讨论】:

  • 谢谢,看起来不错,我会玩一下,同时给你检查!
  • 我无法告诉你我已经从中得到了多少用处,谢谢!查看上面的基本实现。
【解决方案2】:

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

【讨论】:

    【解决方案3】:
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      类 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多