Python:轻松访问深层嵌套的dict(获取和设置) - 代码日志

Python:轻松访问深层嵌套的dict(获取和设置)

我正在构建一些Python代码来读取和操纵深入嵌套的代码(最终用于与JSON服务进行交互,但是对于其他目的而言非常有用)我正在寻找一种方法来轻松地读/设置/更新dict,而不需要很多代码。

@see也是Python: Recursively access dict via attributes as well as index access? – Curt Hagenlocher的“DotDictify”解决方案非常雄辩。我也喜欢本·阿尔曼在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

任何想法,如果这是可能的,如果是这样,怎么去修改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

这种相互作用对于处理深入嵌套的人物而言将是巨大的。有谁知道另一个策略(或示例代码段/库)来尝试?

属性树

您的第一个规范的问题是Python在__getitem__中无法确定,如果在my_obj.abcd中,您将继续下一个不存在的树,在这种情况下,需要使用__getitem__方法返回一个对象,因此您不会得到一个AttributeError抛出你,或者你想要一个值,在这种情况下,它需要返回None。

我会认为,在每一种情况下,你都有以上的,你应该期望它抛出一个KeyError而不是返回None。原因是您无法确定“无”是指“无钥匙”或“某人实际上在该位置无存储”。对于这种行为,你需要做的就是点dotdictify,删除标记,并用__getitem__替换为:

def __getitem__(self, key):
    return self[key]

因为你真正想要的是一个具有__getattr__和__setattr__的dict。

可能有一种方法可以完全删除__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)

我不喜欢原始dotdictify中的标记业务。

路径支持

第二个规范(覆盖get()和set())是一个普通的dict有一个get()的操作与你所描述的不同,甚至没有一个set(尽管它有一个setdefault()操作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__

测试代码:

>>> 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}}
http://stackoverflow.com/questions/3797957/python-easily-access-deeply-nested-dict-get-and-set

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:Python:轻松访问深层嵌套的dict(获取和设置)