我正在构建一些Python代码来读取和操作深层嵌套的dicts(最终用于与JSON服务进行交互,但是出于其他目的它会很棒)我正在寻找一种方法来轻松读取/设置/更新内部值dict,不需要很多代码.
@see还有Python:通过属性递归访问dict以及索引访问? - Curt Hagenlocher的"DotDictify"解决方案非常有说服力.我也喜欢Ben Alman在http://benalman.com/projects/jquery-getobject-plugin/中为JavaScript提供的内容. 将两者结合起来会很棒.
在Curt Hagenlocher和Ben Alman的例子的基础上,拥有如下功能的Python会很棒:
>>> my_obj = DotDictify()
>>> my_obj.a.b.c = {'d':1, 'e':2}
>>> print my_obj
{'a': {'b': {'c': {'d': 1, 'e': 2}}}}
>>> print my_obj.a.b.c.d
1
>>> print my_obj.a.b.c.x
None
>>> print my_obj.a.b.c.d.x
None
>>> print my_obj.a.b.c.d.x.y.z
None
Run Code Online (Sandbox Code Playgroud)
知道这是否可行,如果可行,如何修改DotDictify解决方案?
或者,可以使get方法接受点符号(并添加补充集方法)但是对象符号确定更清晰.
>>> my_obj = DotDictify()
>>> my_obj.set('a.b.c', {'d':1, 'e':2})
>>> print my_obj
{'a': {'b': {'c': {'d': 1, 'e': 2}}}}
>>> print my_obj.get('a.b.c.d')
1
>>> print my_obj.get('a.b.c.x')
None
>>> print my_obj.get('a.b.c.d.x')
None
>>> print my_obj.get('a.b.c.d.x.y.z')
None
Run Code Online (Sandbox Code Playgroud)
这种类型的交互对于处理深层嵌套的dicts非常有用.有人知道另一种策略(或示例代码片段/库)吗?
Mik*_*one 33
你的第一个规范的问题是,Python不能诉说__getitem__,如果在my_obj.a.b.c.d,你接下来会进一步继续向下一个不存在的树,在这种情况下,需要用返回一个对象__getitem__的方法,所以你不会得到AttributeError你抛出,或者如果你想要一个值,在这种情况下它需要返回None.
我认为,无论你在上面的每一种情况下,都应该期待它抛出KeyError而不是返回None.原因是你无法分辨是否None意味着"无钥匙"或"某人实际存放None在该位置".对于此行为,您所要做的就是使用dotdictify,删除marker和替换__getitem__:
def __getitem__(self, key):
return self[key]
Run Code Online (Sandbox Code Playgroud)
因为你真正想要的是一个dict有__getattr__和__setattr__.
可能有一种方法可以__getitem__完全删除并说出类似的内容__getattr__ = dict.__getitem__,但我认为这可能是过度优化,如果您以后决定要__getitem__像dotdictify最初那样创建树,那将会成为问题,在这种情况下,您会将其更改为:
def __getitem__(self, key):
if key not in self:
dict.__setitem__(self, key, dotdictify())
return dict.__getitem__(self, key)
Run Code Online (Sandbox Code Playgroud)
我不喜欢marker原来的生意dotdictify.
第二个规范(覆盖get()和set())是法线dict的get()运行方式与您描述的不同,甚至没有set(尽管它具有与之setdefault()相反的操作get()).人们期望get采用两个参数,如果找不到密钥则第二个是默认参数.
如果要扩展__getitem__并__setitem__处理虚线键表示法,则需要修改doctictify为:
class dotdictify(dict):
def __init__(self, value=None):
if value is None:
pass
elif isinstance(value, dict):
for key in value:
self.__setitem__(key, value[key])
else:
raise TypeError, 'expected dict'
def __setitem__(self, key, value):
if '.' in key:
myKey, restOfKey = key.split('.', 1)
target = self.setdefault(myKey, dotdictify())
if not isinstance(target, dotdictify):
raise KeyError, 'cannot set "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
target[restOfKey] = value
else:
if isinstance(value, dict) and not isinstance(value, dotdictify):
value = dotdictify(value)
dict.__setitem__(self, key, value)
def __getitem__(self, key):
if '.' not in key:
return dict.__getitem__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not isinstance(target, dotdictify):
raise KeyError, 'cannot get "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
return target[restOfKey]
def __contains__(self, key):
if '.' not in key:
return dict.__contains__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not isinstance(target, dotdictify):
return False
return restOfKey in target
def setdefault(self, key, default):
if key not in self:
self[key] = default
return self[key]
__setattr__ = __setitem__
__getattr__ = __getitem__
Run Code Online (Sandbox Code Playgroud)
测试代码:
>>> life = dotdictify({'bigBang': {'stars': {'planets': {}}}})
>>> life.bigBang.stars.planets
{}
>>> life.bigBang.stars.planets.earth = { 'singleCellLife' : {} }
>>> life.bigBang.stars.planets
{'earth': {'singleCellLife': {}}}
>>> life['bigBang.stars.planets.mars.landers.vikings'] = 2
>>> life.bigBang.stars.planets.mars.landers.vikings
2
>>> 'landers.vikings' in life.bigBang.stars.planets.mars
True
>>> life.get('bigBang.stars.planets.mars.landers.spirit', True)
True
>>> life.setdefault('bigBang.stars.planets.mars.landers.opportunity', True)
True
>>> 'landers.opportunity' in life.bigBang.stars.planets.mars
True
>>> life.bigBang.stars.planets.mars
{'landers': {'opportunity': True, 'vikings': 2}}
Run Code Online (Sandbox Code Playgroud)
对于谷歌员工:我们现在有瘾君子:
pip install addict
Run Code Online (Sandbox Code Playgroud)
和
mapping.a.b.c.d.e = 2
mapping
{'a': {'b': {'c': {'d': {'e': 2}}}}}
Run Code Online (Sandbox Code Playgroud)
我广泛地使用它。
为了使用虚线路径,我发现dotted:
obj = DottedDict({'hello': {'world': {'wide': 'web'}}})
obj['hello.world.wide'] == 'web' # true
Run Code Online (Sandbox Code Playgroud)
较旧的答案中有一些非常好的提示,但它们都需要用自定义数据结构替换标准 Python 数据结构(dicts 等),并且不适用于不是有效属性名称的键。
这些天我们可以做得更好,使用纯 Python、Python 2/3 兼容库,专门为此目的而构建,称为glom。使用您的示例:
import glom
target = {} # a plain dictionary we will deeply set on
glom.assign(target, 'a.b.c', {'d': 1, 'e': 2}, missing=dict)
# {'a': {'b': {'c': {'e': 2, 'd': 1}}}}
Run Code Online (Sandbox Code Playgroud)
注意missing=dict, 用于自动创建字典。我们可以使用 glom 的 deep-get 轻松获取值:
glom.glom(target, 'a.b.c.d')
# 1
Run Code Online (Sandbox Code Playgroud)
你可以用glom做更多的事情,尤其是在深度获取和设置方面。我应该知道,因为(完全披露)我创建了它。这意味着如果你发现一个缺口,你应该让我知道!