理想情况下,我希望实现的是一个扩展(或非常类似)Python中dict
的类,具有附加功能:
dict
(即setitem,getitem)我的目标是,如果我有类似example = DotDict()
的内容,我可以针对它example.configuration.first= 'first'
执行以下操作,它会在example
下实例化相应的DotDict实例,其中非常痛苦的警告是该操作不是分配,它应该只是像KeyError
那样提出dict
这是我天真的组装
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之外的其他解释器上完成,我会很好奇。
答案 0 :(得分: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
答案 1 :(得分:1)
根据用户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)
答案 2 :(得分: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
}
}
>>>
答案 3 :(得分:0)
答案 4 :(得分: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