理想情况下,我的目标是dict
在 Python 中扩展(或非常相似) a 并具有附加功能的类:
- 点符号能够设置和获取值
- 键值功能,例如
dict
(即 setitem、getitem) - 可以链接点表示的操作
目标是,如果我有类似的事情example = DotDict()
可以针对它执行以下操作,example.configuration.first= 'first'
并且它将实例化适当的 DotDict 实例example
,但真正痛苦的警告是,如果操作不是赋值,它应该简单地引发 a KeyError
like a dict
would 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 之外的其他解释器上完成。