Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:轻松访问深度嵌套的dict(get和set)_Python - Fatal编程技术网

Python:轻松访问深度嵌套的dict(get和set)

Python:轻松访问深度嵌套的dict(get和set),python,Python,我正在构建一些Python代码来读取和操作深度嵌套的dict(最终是为了与JSON服务交互,但是如果有其他用途,那就太好了),我正在寻找一种方法来轻松读取/设置/更新dict中的深度值,而不需要大量代码 @另请参见——Curt Hagenlocher的“Dotdicify”解决方案相当雄辩。我也喜欢Ben Alman在《将两者结合在一起》中为JavaScript提供的内容 以Curt Hagenlocher和Ben Alman的示例为基础,在Python中拥有如下功能将是非常棒的: >&g

我正在构建一些Python代码来读取和操作深度嵌套的dict(最终是为了与JSON服务交互,但是如果有其他用途,那就太好了),我正在寻找一种方法来轻松读取/设置/更新dict中的深度值,而不需要大量代码

@另请参见——Curt Hagenlocher的“Dotdicify”解决方案相当雄辩。我也喜欢Ben Alman在《将两者结合在一起》中为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

这种类型的交互对于处理深度嵌套的dict非常有用。有人知道另一种策略(或示例代码段/库)可以尝试吗?

我曾使用过类似的方法来为应用程序构建类似的somathing。我希望有帮助

class Trie:
    """
    A Trie is like a dictionary in that it maps keys to values.
    However, because of the way keys are stored, it allows
    look up based on the longest prefix that matches.

    """

    def __init__(self):
        # Every node consists of a list with two position.  In
        # the first one,there is the value while on the second
        # one a dictionary which leads to the rest of the nodes.
        self.root = [0, {}]


    def insert(self, key):
        """
        Add the given value for the given key.

        >>> a = Trie()
        >>> a.insert('kalo')
        >>> print(a)
        [0, {'k': [1, {'a': [1, {'l': [1, {'o': [1, {}]}]}]}]}]
        >>> a.insert('kalo')
        >>> print(a)
        [0, {'k': [2, {'a': [2, {'l': [2, {'o': [2, {}]}]}]}]}]
        >>> b = Trie()
        >>> b.insert('heh')
        >>> b.insert('ha')
        >>> print(b)
        [0, {'h': [2, {'a': [1, {}], 'e': [1, {'h': [1, {}]}]}]}]

        """

        # find the node to append the new value.
        curr_node = self.root
        for k in key:
            curr_node = curr_node[1].setdefault(k, [0, {}])
            curr_node[0] += 1


    def find(self, key):
        """
        Return the value for the given key or None if key not
        found.

        >>> a = Trie()
        >>> a.insert('ha')
        >>> a.insert('ha')
        >>> a.insert('he')
        >>> a.insert('ho')
        >>> print(a.find('h'))
        4
        >>> print(a.find('ha'))
        2
        >>> print(a.find('he'))
        1

        """

        curr_node = self.root
        for k in key:
            try:
                curr_node = curr_node[1][k]
            except KeyError:
                return 0
        return curr_node[0]

    def __str__(self):
        return str(self.root)

    def __getitem__(self, key):
        curr_node = self.root
        for k in key:
            try:
                curr_node = curr_node[1][k]
            except KeyError:
                yield None
        for k in curr_node[1]:
            yield k, curr_node[1][k][0]

if __name__ == '__main__':
    a = Trie()
    a.insert('kalo')
    a.insert('kala')
    a.insert('kal')
    a.insert('kata')
    print(a.find('kala'))
    for b in a['ka']:
        print(b)
    print(a)
属性树 您的第一个规范的问题是,Python无法在
\uuuu getitem\uuuuu
中判断,如果在
my_obj.a.b.c.d
中,您接下来将在一个不存在的树下继续进行,在这种情况下,它需要返回一个带有
\uu getitem\uuuuuuu
方法的对象,这样您就不会得到一个向您抛出的
AttributeError
,或者,如果需要值,则需要返回
None

我认为,在上面的每种情况下,您都应该期望它抛出一个
KeyError
,而不是返回
None
。原因是您无法判断
None
是否表示“没有钥匙”或“有人实际将
None
存储在该位置”。对于此行为,您所要做的就是使用
dotdictify
,删除
标记
,并将
\uu getitem\uuuuuuuu
替换为:

def __getitem__(self, key):
    return self[key]
因为您真正想要的是一个带有
命令

可能有一种方法可以完全删除
\uuuu getitem\uuuuuu
,并说一些类似于
\uuu getattr\uuuuuu=dict的话。但是我认为这可能是过度优化了,如果您以后决定要
\uuu getitem\uuuuuu
像最初那样创建树,这将是一个问题,在这种情况下,您可以将其更改为:

def __getitem__(self, key):
    if key not in self:
        dict.__setitem__(self, key, dotdictify())
    return dict.__getitem__(self, key)
我不喜欢原始
dotdicify
中的
标记
业务

路径支持 第二个规范(覆盖
get()
set()
)是一个普通的
dict
有一个
get()
,它的操作与您描述的不同,甚至没有
集(尽管它有一个
setdefault()
,这是
get()
的反向操作)。人们希望
get
接受两个参数,如果找不到键,则第二个参数是默认参数

如果要扩展
\uuuu getitem\uuuuuuuuuuuu
\uuuuuuuu setitem\uuuuuuuuuuuuu
以处理虚线键符号,则需要将
docticify
修改为:

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}}

谷歌同行:我们现在有:

我广泛使用它

要使用虚线路径,我发现:


以前的答案中有一些非常好的技巧,但是它们都需要用自定义的数据结构替换标准的Python数据结构(dicts等),并且不适用于无效属性名的键

现在,我们可以做得更好,使用一个纯Python的、与Python 2/3兼容的库,这个库就是为此目的而构建的,名为。以您的例子:

导入glom
target={}一本我们将深入探讨的普通词典
glom.assign(目标'a.b.c',{'d':1',e':2},missing=dict)
#{'a':{'b':{'c':{'e':2,'d':1}}
请注意用于自动创建词典的
missing=dict
。我们可以使用glom的deep get轻松获取价值:

glom.glom(target, 'a.b.c.d')
# 1

你还可以做更多的事情,尤其是在深度挖掘和设置方面。我应该知道,因为(完全披露)是我创造的。这意味着如果你发现了一个差距,你应该

非常感谢,迈克。我添加了一个接受点表示法的get函数(正如您所指出的,这是一个默认值),我认为这个新的dotdicify类将使处理深度嵌套的dict变得更加容易。非常感谢。您需要
get()
函数吗?现有的
get()
没有做什么?您在问题中描述的
get()
相当于您从
dict
免费获得的
get(key,None)
。当我使用dotdictify类“as is”w/my Python 2.5安装(Google App Engine SDK)时,get函数出于某种原因没有处理点表示法请求。因此,我为get()函数编写了一个快速包装器来检查点符号,如果是,则传递给getattr(在异常时返回默认值),否则传递给dict.get(self、key、default)@Mike DeSimone我开始使用你的dotdictify代码,但当我使用dotdictify的嵌套dict时遇到了一些问题,然后我尝试使用点符号访问嵌套字段。更改的要点如下:如果您对测试用例感兴趣,请让我知道。只是对我的要点做了另一个更改,以适应key为None时的情况——是的,一个不好的边缘情况,但仍然是一个真实的情况:)在中看到一个更简单的答案,您可以使用非常简单的包dict deep,它具有deep_get和deep_set函数。键可以是点符号“a.b.c”的字符串,也可以是list()构造函数接受的任何内容。[免责声明:我是dict deep的作者]是一个非常好的库,支持嵌套的dict和list.d=dict({'a':{'ab':'sa'});d['a.ab']在给瘾君子一本空字典,我做错什么了吗?
mapping.a.b.c.d.e = 2
mapping
{'a': {'b': {'c': {'d': {'e': 2}}}}}
obj = DottedDict({'hello': {'world': {'wide': 'web'}}})
obj['hello.world.wide'] == 'web'  # true
glom.glom(target, 'a.b.c.d')
# 1