理想情况下,我想要实现的是一个类,它可以扩展(或者非常类似于)Python中的
以下是我最初组装的代码:
除了一些常见的边缘情况外,它可以正常工作。我必须说,我非常讨厌这种方法,肯定有更好的方法。查看堆栈并在最后一行上运行正则表达式并不是实现此目标的好方法。
关键问题是,Python将代码行从左到右解释,因此当它到达类似于 a.b.c = 3 的语句时,它的第一个操作是 getattr(a, b),而不是 setattr,因此我无法轻松确定堆栈操作中的最后一个操作是否为赋值。
我想知道的是是否有一种好的方法来确定操作堆栈中的最后一个操作,或者至少它是否为setattr。
编辑:
这是我根据user1320237的建议提出的解决方案。
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 之外的其他解释器上完成。
AttributeError
而不是KeyError
,否则您的代码将无法与hasattr
等一起使用。 - Antti Haapala -- Слава Україні