Python 如何完全遍历未知深度的复杂字典?

Python 如何完全遍历未知深度的复杂字典?,python,json,dictionary,python-2.7,Python,Json,Dictionary,Python 2.7,从JSON导入可以得到非常复杂的嵌套结构。 例如: {u'body': [{u'declarations': [{u'id': {u'name': u'i', u'type': u'Identifier'}, u'init': {u'type': u'Literal', u'value': 2},

JSON
导入可以得到非常复杂的嵌套结构。 例如:

{u'body': [{u'declarations': [{u'id': {u'name': u'i',
                                       u'type': u'Identifier'},
                               u'init': {u'type': u'Literal', u'value': 2},
                               u'type': u'VariableDeclarator'}],
            u'kind': u'var',
            u'type': u'VariableDeclaration'},
           {u'declarations': [{u'id': {u'name': u'j',
                                       u'type': u'Identifier'},
                               u'init': {u'type': u'Literal', u'value': 4},
                               u'type': u'VariableDeclarator'}],
            u'kind': u'var',
            u'type': u'VariableDeclaration'},
           {u'declarations': [{u'id': {u'name': u'answer',
                                       u'type': u'Identifier'},
                               u'init': {u'left': {u'name': u'i',
                                                   u'type': u'Identifier'},
                                         u'operator': u'*',
                                         u'right': {u'name': u'j',
                                                    u'type': u'Identifier'},
                                         u'type': u'BinaryExpression'},
                               u'type': u'VariableDeclarator'}],
            u'kind': u'var',
            u'type': u'VariableDeclaration'}],
 u'type': u'Program'}
建议以什么方式行走上述复杂结构


除了少数几个列表外,大部分都是字典,结构可能会变得更加复杂,因此我需要一个通用的解决方案。

如果您只需要遍历字典,我建议使用递归的
walk
函数,该函数获取字典,然后递归遍历其元素。大概是这样的:

def walk(node):
    for key, item in node.items():
        if item is a collection:
            walk(item)
        else:
            It is a leaf, do your thing
Other.Stuff.Here.Key=Value 
root1.height=1.9 
root1.surname=Fabiano 
root1.name=Silos 
root1.address.country=Brazil 
root1.address.x=Pinheiros 
root1.address.city=Sao 
root2.height=1.78 
root2.surname=My 
root2.name=Friend 
root2.address.country=Brazil 
root2.address.detail.neighbourhood=Central 
root2.address.city=Recife 

如果您还想搜索元素,或查询通过某些条件的多个元素,请查看该模块。

您可以使用递归生成器将字典转换为平面列表

def dict_generator(indict, pre=None):
    pre = pre[:] if pre else []
    if isinstance(indict, dict):
        for key, value in indict.items():
            if isinstance(value, dict):
                for d in dict_generator(value, pre + [key]):
                    yield d
            elif isinstance(value, list) or isinstance(value, tuple):
                for v in value:
                    for d in dict_generator(v, pre + [key]):
                        yield d
            else:
                yield pre + [key, value]
    else:
        yield pre + [indict]
它回来了

[u'body', u'kind', u'var']
[u'init', u'declarations', u'body', u'type', u'Literal']
[u'init', u'declarations', u'body', u'value', 2]
[u'declarations', u'body', u'type', u'VariableDeclarator']
[u'id', u'declarations', u'body', u'type', u'Identifier']
[u'id', u'declarations', u'body', u'name', u'i']
[u'body', u'type', u'VariableDeclaration']
[u'body', u'kind', u'var']
[u'init', u'declarations', u'body', u'type', u'Literal']
[u'init', u'declarations', u'body', u'value', 4]
[u'declarations', u'body', u'type', u'VariableDeclarator']
[u'id', u'declarations', u'body', u'type', u'Identifier']
[u'id', u'declarations', u'body', u'name', u'j']
[u'body', u'type', u'VariableDeclaration']
[u'body', u'kind', u'var']
[u'init', u'declarations', u'body', u'operator', u'*']
[u'right', u'init', u'declarations', u'body', u'type', u'Identifier']
[u'right', u'init', u'declarations', u'body', u'name', u'j']
[u'init', u'declarations', u'body', u'type', u'BinaryExpression']
[u'left', u'init', u'declarations', u'body', u'type', u'Identifier']
[u'left', u'init', u'declarations', u'body', u'name', u'i']
[u'declarations', u'body', u'type', u'VariableDeclarator']
[u'id', u'declarations', u'body', u'type', u'Identifier']
[u'id', u'declarations', u'body', u'name', u'answer']
[u'body', u'type', u'VariableDeclaration']
[u'type', u'Program']

更新:注释中提到的从
[key]+pre
pre+[key]
的固定键列表。

如果您知道数据的含义,可能需要创建一个
解析
函数,将嵌套容器转换为自定义类型的对象树。然后,您将使用这些自定义对象的方法来处理数据

对于示例数据结构,您可以创建
程序
变量声明
变量声明器
标识符
文字
二进制表达式
类,然后对解析器使用类似的内容:

def parse(d):
    t = d[u"type"]

    if t == u"Program":
        body = [parse(block) for block in d[u"body"]]
        return Program(body)

    else if t == u"VariableDeclaration":
        kind = d[u"kind"]
        declarations = [parse(declaration) for declaration in d[u"declarations"]]
        return VariableDeclaration(kind, declarations)

    else if t == u"VariableDeclarator":
        id = parse(d[u"id"])
        init = parse(d[u"init"])
        return VariableDeclarator(id, init)

    else if t == u"Identifier":
        return Identifier(d[u"name"])

    else if t == u"Literal":
        return Literal(d[u"value"])

    else if t == u"BinaryExpression":
        operator = d[u"operator"]
        left = parse(d[u"left"])
        right = parse(d[u"right"])
        return BinaryExpression(operator, left, right)

    else:
        raise ValueError("Invalid data structure.")

根据任务的不同,您可以从标准库
json
模块扩展编码器和解码器,而不是编写自己的解析器

我建议这样做,特别是当您需要将属于自定义类的对象编码到json中时。如果必须做一些操作,也可以在JSON的字符串表示上进行,那么也要考虑JSONECODE()的迭代。 对于这两种情况,参考是

可能会有帮助:

def walk(d):
    global path
      for k,v in d.items():
          if isinstance(v, str) or isinstance(v, int) or isinstance(v, float):
            path.append(k)
            print "{}={}".format(".".join(path), v)
            path.pop()
          elif v is None:
            path.append(k)
            ## do something special
            path.pop()
          elif isinstance(v, dict):
            path.append(k)
            walk(v)
            path.pop()
          else:
            print "###Type {} not recognized: {}.{}={}".format(type(v), ".".join(path),k, v)

mydict = {'Other': {'Stuff': {'Here': {'Key': 'Value'}}}, 'root1': {'address': {'country': 'Brazil', 'city': 'Sao', 'x': 'Pinheiros'}, 'surname': 'Fabiano', 'name': 'Silos', 'height': 1.9}, 'root2': {'address': {'country': 'Brazil', 'detail': {'neighbourhood': 'Central'}, 'city': 'Recife'}, 'surname': 'My', 'name': 'Friend', 'height': 1.78}}

path = []
walk(mydict)
将产生如下输出:

def walk(node):
    for key, item in node.items():
        if item is a collection:
            walk(item)
        else:
            It is a leaf, do your thing
Other.Stuff.Here.Key=Value 
root1.height=1.9 
root1.surname=Fabiano 
root1.name=Silos 
root1.address.country=Brazil 
root1.address.x=Pinheiros 
root1.address.city=Sao 
root2.height=1.78 
root2.surname=My 
root2.name=Friend 
root2.address.country=Brazil 
root2.address.detail.neighbourhood=Central 
root2.address.city=Recife 

对上述解决方案的一些补充(处理json,包括列表)


如果接受的答案对您有效,但您也希望使用包含嵌套数组数字索引的完整、有序的路径,那么这种细微的变化将起作用:

def dict_生成器(indicate,pre=None):
pre=pre[:]如果pre-else[]
如果不存在(起诉、命令):
对于键,indicate.items()中的值:
如果isinstance(值,dict):
对于dict_生成器中的d(值,前置+[键]):
产量d
elif isinstance(值,列表)或isinstance(值,元组):
对于枚举中的k,v(值):
对于dict_生成器中的d(v,pre+[key]+[k]):
产量d
其他:
收益率前+[键,值]
其他:
产量指标

我更灵活的版本如下:

def walktree(树,at=lambda节点:非isinstance(节点,dict),前缀=(),
FlattNode=lambda节点:isinstance(节点,(列表、元组、集合)):
"""
遍历树,并返回从根节点到叶节点的路径的迭代器。
树:像“{'a':{'b':1,'c':2}”
at:bool函数或int表示要降低的级别num。walktree(tree,at=1)相当于tree.items()
FlattNode:一个bool函数,用于决定是否在节点值处迭代
"""
如果isinstance(at,int):
isleaf_Uat==0
isleaf=λv:isleaf_
at=at-1
其他:
isleaf=at
如果是isleaf(树):
如果未展平节点(树):
产量(*前缀,树)
其他:
对于树中的v:
walktree的产量(v、at、前缀、FlattNode=FlattNode)
其他:
对于树中的k,v.items():
来自walktree的产量(v,at,(*前缀,k),FlattNode=FlattNode)
用法:

>列表(walktree({'a':{'b':[0,1],'c':2},'d':3}))
[('a','b',0),('a','b',1),('a','c',2),('d',3)]
>列表(walktree({'a':{'b':[0,1],'c':2},'d':3},flattNode=lambda e:False))
[('a','b',[0,1]),('a','c',2),('d',3)]
>列表(walktree({'a':{'b':[0,1],'c':2},'d':3},at=1))
[('a',{'b':[0,1],'c':2}),('d',3)]
>列表(walktree({'a':{'b':[0,{1:9,9:1}],'c':2},'d':3}))
[('a','b',0),('a','b',1,9),('a','b',9,1),('a','c',2),('d',3)]
>列表(walktree([1,2,3,4,5]]))
[(1,), (2,), (3,), (4,), (5,)]

您想用字典做什么?这只适用于直接嵌套的字典。在示例数据结构中,有几个字典值是其他字典的列表。需要一些额外的逻辑来处理这些问题(例如在列表理解或生成器表达式中递归)。为了使事情正常运行,您可能需要使用您对数据含义的知识,例如所有字典都有一个“type”键。您还应该仔细检查循环。e、 g.:foo={};foo[bar]=foo会把你炸飞的。您的walk函数应该保留它所看到的对象的列表,而不是下降。(您可以将列表传递给每个后续调用)如果项是集合,则抛出错误。您的导入声明是什么?我正在阅读的所有解决方案中,这是最简单的,效果很好:-)感谢Bryukhanov ValentinGreat解决方案,但您有一个错误,“[key]+pre”应该是“pre+[key]”。否则返回的列表会使路径变得混乱。我还想在列表/元组child的情况下获取索引。我通过添加
enumerate
实现了这一点,并做了如下操作:
对于枚举中的I,v(value):对于dict_生成器中的d(v,pre+[key+f'[{I}]'):
Full:(它是Python,而不是C#)它工作得很好。它生成树中路径的完整列表。每个列表一条路径。mypaths=[*dict_generator(myjson)]对于mypaths:print(path)中的路径,请考虑避免发布包含大量内容的长答案code@Khaled代码的大小,在我看来,代码对于所需的内容来说是最小的