简体   繁体   English

循环遍历所有嵌套的字典值?

[英]Loop through all nested dictionary values?

for k, v in d.iteritems():
    if type(v) is dict:
        for t, c in v.iteritems():
            print "{0} : {1}".format(t, c)

I'm trying to loop through a dictionary and print out all key value pairs where the value is not a nested dictionary.我正在尝试遍历字典并打印出值不是嵌套字典的所有键值对。 If the value is a dictionary I want to go into it and print out its key value pairs...etc.如果该值是一个字典,我想进入它并打印出它的键值对......等等。 Any help?有什么帮助吗?

EDIT编辑

How about this?这个怎么样? It still only prints one thing.它仍然只打印一件事。

def printDict(d):
    for k, v in d.iteritems():
        if type(v) is dict:
            printDict(v)
        else:
            print "{0} : {1}".format(k, v)

Full Test Case完整的测试用例

Dictionary:字典:

{u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'},
      u'port': u'11'}}

Result:结果:

xml : {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}

As said by Niklas, you need recursion, ie you want to define a function to print your dict, and if the value is a dict, you want to call your print function using this new dict.正如 Niklas 所说,你需要递归,即你想定义一个函数来打印你的 dict,如果值是一个 dict,你想使用这个新的 dict 调用你的打印函数。

Something like :就像是 :

def myprint(d):
    for k, v in d.items():
        if isinstance(v, dict):
            myprint(v)
        else:
            print("{0} : {1}".format(k, v))

There are potential problems if you write your own recursive implementation or the iterative equivalent with stack.如果您编写自己的递归实现或使用堆栈的迭代等效项,则可能会出现问题 See this example:看这个例子:

dic = {}
dic["key1"] = {}
dic["key1"]["key1.1"] = "value1"
dic["key2"]  = {}
dic["key2"]["key2.1"] = "value2"
dic["key2"]["key2.2"] = dic["key1"]
dic["key2"]["key2.3"] = dic

In the normal sense, nested dictionary will be a n-nary tree like data structure.在正常意义上,嵌套字典将是一个类似 n 元树的数据结构。 But the definition doesn't exclude the possibility of a cross edge or even a back edge (thus no longer a tree).但是该定义不排除交叉边缘甚至后边缘的可能性(因此不再是树)。 For instance, here key2.2 holds to the dictionary from key1 , key2.3 points to the entire dictionary(back edge/cycle).例如,这里key2.2保存来自key1的字典, key2.3指向整个字典(后边缘/循环)。 When there is a back edge(cycle), the stack/recursion will run infinitely.当存在后沿(循环)时,堆栈/递归将无限运行。

            root<-------back edge
          /      \           |
       _key1   __key2__      |
      /       /   \    \     |
 |->key1.1 key2.1 key2.2 key2.3
 |   /       |      |
 | value1  value2   |
 |                  | 
cross edge----------|

If you print this dictionary with this implementation from Scharron如果您使用Scharron的此实现打印此字典

def myprint(d):
    for k, v in d.items():
        if isinstance(v, dict):
            myprint(v)
        else:
            print "{0} : {1}".format(k, v)
            

You would see this error:你会看到这个错误:

> RuntimeError: maximum recursion depth exceeded while calling a Python object

The same goes with the implementation from senderle . senderle的实现也是如此。

Similarly, you get an infinite loop with this implementation from Fred Foo :同样,您会从Fred Foo获得这个实现的无限循环:

def myprint(d):
    stack = list(d.items())
    while stack:
        k, v = stack.pop()
        if isinstance(v, dict):
            stack.extend(v.items())
        else:
            print("%s: %s" % (k, v))

However, Python actually detects cycles in nested dictionary:然而,Python 实际上检测嵌套字典中的循环:

print dic
{'key2': {'key2.1': 'value2', 'key2.3': {...}, 
       'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}

"{...}" is where a cycle is detected. “{...}”是检测到循环的地方。

As requested by Moondra this is a way to avoid cycles (DFS):根据Moondra的要求,这是一种避免循环 (DFS) 的方法:

def myprint(d): 
    stack = list(d.items()) 
    visited = set() 
    while stack: 
        k, v = stack.pop() 
        if isinstance(v, dict): 
            if k not in visited: 
                stack.extend(v.items()) 
        else: 
            print("%s: %s" % (k, v)) 
        visited.add(k)

Since a dict is iterable, you can apply the classic nested container iterable formula to this problem with only a couple of minor changes.由于dict是可迭代的,因此您可以将经典的嵌套容器可迭代公式应用于此问题,只需进行一些小的更改。 Here's a Python 2 version (see below for 3):这是一个 Python 2 版本(见下文 3):

import collections
def nested_dict_iter(nested):
    for key, value in nested.iteritems():
        if isinstance(value, collections.Mapping):
            for inner_key, inner_value in nested_dict_iter(value):
                yield inner_key, inner_value
        else:
            yield key, value

Test:测试:

list(nested_dict_iter({'a':{'b':{'c':1, 'd':2}, 
                            'e':{'f':3, 'g':4}}, 
                       'h':{'i':5, 'j':6}}))
# output: [('g', 4), ('f', 3), ('c', 1), ('d', 2), ('i', 5), ('j', 6)]

In Python 2, It might be possible to create a custom Mapping that qualifies as a Mapping but doesn't contain iteritems , in which case this will fail.在 Python 2 中,可能可以创建一个自定义Mapping ,它符合Mapping但不包含iteritems的条件,在这种情况下,这将失败。 The docs don't indicate that iteritems is required for a Mapping ;文档没有表明Mapping需要iteritems on the other hand, the source gives Mapping types an iteritems method.另一方面, sourceMapping类型提供了一个iteritems方法。 So for custom Mappings , inherit from collections.Mapping explicitly just in case.因此,对于自定义Mappings ,明确地从collections.Mapping继承以防万一。

In Python 3, there are a number of improvements to be made.在 Python 3 中,需要进行许多改进。 As of Python 3.3, abstract base classes live in collections.abc .从 Python 3.3 开始,抽象基类位于collections.abc中。 They remain in collections too for backwards compatibility, but it's nicer having our abstract base classes together in one namespace.为了向后兼容,它们也保留在collections中,但是将我们的抽象基类放在一个命名空间中会更好。 So this imports abc from collections .所以这从collections导入abc Python 3.3 also adds yield from , which is designed for just these sorts of situations. Python 3.3 还增加了yield from ,它就是为这类情况而设计的。 This is not empty syntactic sugar;这不是空的语法糖; it may lead to faster code and more sensible interactions with coroutines .它可能会导致更快的代码和更合理的协同程序交互。

from collections import abc
def nested_dict_iter(nested):
    for key, value in nested.items():
        if isinstance(value, abc.Mapping):
            yield from nested_dict_iter(value)
        else:
            yield key, value

Alternative iterative solution:替代迭代解决方案:

def myprint(d):
    stack = d.items()
    while stack:
        k, v = stack.pop()
        if isinstance(v, dict):
            stack.extend(v.iteritems())
        else:
            print("%s: %s" % (k, v))

Slightly different version I wrote that keeps track of the keys along the way to get there我写的稍微不同的版本,可以跟踪到达那里的钥匙

def print_dict(v, prefix=''):
    if isinstance(v, dict):
        for k, v2 in v.items():
            p2 = "{}['{}']".format(prefix, k)
            print_dict(v2, p2)
    elif isinstance(v, list):
        for i, v2 in enumerate(v):
            p2 = "{}[{}]".format(prefix, i)
            print_dict(v2, p2)
    else:
        print('{} = {}'.format(prefix, repr(v)))

On your data, it'll print在您的数据上,它会打印

data['xml']['config']['portstatus']['status'] = u'good'
data['xml']['config']['target'] = u'1'
data['xml']['port'] = u'11'

It's also easy to modify it to track the prefix as a tuple of keys rather than a string if you need it that way.如果您需要这种方式,也可以轻松修改它以将前缀作为键元组而不是字符串来跟踪。

Here is pythonic way to do it.这是pythonic的方法。 This function will allow you to loop through key-value pair in all the levels.此函数将允许您循环遍历所有级别的键值对。 It does not save the whole thing to the memory but rather walks through the dict as you loop through it它不会将整个内容保存到内存中,而是在您遍历字典时遍历它

def recursive_items(dictionary):
    for key, value in dictionary.items():
        if type(value) is dict:
            yield (key, value)
            yield from recursive_items(value)
        else:
            yield (key, value)

a = {'a': {1: {1: 2, 3: 4}, 2: {5: 6}}}

for key, value in recursive_items(a):
    print(key, value)

Prints印刷

a {1: {1: 2, 3: 4}, 2: {5: 6}}
1 {1: 2, 3: 4}
1 2
3 4
2 {5: 6}
5 6

A alternative solution to work with lists based on Scharron's solution使用基于 Scharron 解决方案的列表的替代解决方案

def myprint(d):
    my_list = d.iteritems() if isinstance(d, dict) else enumerate(d)

    for k, v in my_list:
        if isinstance(v, dict) or isinstance(v, list):
            myprint(v)
        else:
            print u"{0} : {1}".format(k, v)

I am using the following code to print all the values of a nested dictionary, taking into account where the value could be a list containing dictionaries.我正在使用以下代码打印嵌套字典的所有值,同时考虑到值可能是包含字典的列表的位置。 This was useful to me when parsing a JSON file into a dictionary and needing to quickly check whether any of its values are None .在将 JSON 文件解析为字典并需要快速检查其任何值是否为None时,这对我很有用。

    d = {
            "user": 10,
            "time": "2017-03-15T14:02:49.301000",
            "metadata": [
                {"foo": "bar"},
                "some_string"
            ]
        }


    def print_nested(d):
        if isinstance(d, dict):
            for k, v in d.items():
                print_nested(v)
        elif hasattr(d, '__iter__') and not isinstance(d, str):
            for item in d:
                print_nested(item)
        elif isinstance(d, str):
            print(d)

        else:
            print(d)

    print_nested(d)

Output:输出:

    10
    2017-03-15T14:02:49.301000
    bar
    some_string

Your question already has been answered well, but I recommend using isinstance(d, collections.Mapping) instead of isinstance(d, dict) .您的问题已经得到很好的回答,但我建议使用isinstance(d, collections.Mapping)而不是isinstance(d, dict) It works for dict() , collections.OrderedDict() , and collections.UserDict() .它适用于dict()collections.OrderedDict()collections.UserDict()

The generally correct version is:一般正确的版本是:

def myprint(d):
    for k, v in d.items():
        if isinstance(v, collections.Mapping):
            myprint(v)
        else:
            print("{0} : {1}".format(k, v))

Iterative solution as an alternative:作为替代方案的迭代解决方案:

def traverse_nested_dict(d):
    iters = [d.iteritems()]

    while iters:
        it = iters.pop()
        try:
            k, v = it.next()
        except StopIteration:
            continue

        iters.append(it)

        if isinstance(v, dict):
            iters.append(v.iteritems())
        else:
            yield k, v


d = {"a": 1, "b": 2, "c": {"d": 3, "e": {"f": 4}}}
for k, v in traverse_nested_dict(d):
    print k, v

Here's a modified version of Fred Foo's answer for Python 2. In the original response, only the deepest level of nesting is output.这是 Fred Foo 对 Python 2 的回答的修改版本。在原始响应中,仅输出最深的嵌套级别。 If you output the keys as lists, you can keep the keys for all levels, although to reference them you need to reference a list of lists.如果将键输出为列表,则可以保留所有级别的键,但要引用它们,您需要引用列表列表。

Here's the function:这是功能:

def NestIter(nested):
    for key, value in nested.iteritems():
        if isinstance(value, collections.Mapping):
            for inner_key, inner_value in NestIter(value):
                yield [key, inner_key], inner_value
        else:
            yield [key],value

To reference the keys:要引用密钥:

for keys, vals in mynested: 
    print(mynested[keys[0]][keys[1][0]][keys[1][1][0]])

for a three-level dictionary.对于三级词典。

You need to know the number of levels before to access multiple keys and the number of levels should be constant (it may be possible to add a small bit of script to check the number of nesting levels when iterating through values, but I haven't yet looked at this).您需要在访问多个键之前知道级别数,并且级别数应该是恒定的(可能可以添加一小段脚本来检查迭代值时的嵌套级别数,但我没有还看了这个)。

I find this approach a bit more flexible, here you just providing generator function that emits key, value pairs and can be easily extended to also iterate over lists.我发现这种方法更灵活一些,在这里您只需提供生成键、值对的生成器函数,并且可以轻松扩展以迭代列表。

def traverse(value, key=None):
    if isinstance(value, dict):
        for k, v in value.items():
            yield from traverse(v, k)
    else:
        yield key, value

Then you can write your own myprint function, then would print those key value pairs.然后您可以编写自己的myprint函数,然后打印这些键值对。

def myprint(d):
    for k, v in traverse(d):
        print(f"{k} : {v}")

A test:一个测试:

myprint({
    'xml': {
        'config': {
            'portstatus': {
                'status': 'good',
            },
            'target': '1',
        },
        'port': '11',
    },
})

Output:输出:

status : good
target : 1
port : 11

I tested this on Python 3.6.我在 Python 3.6 上对此进行了测试。

These answers work for only 2 levels of sub-dictionaries.这些答案仅适用于 2 个级别的子词典。 For more try this:更多试试这个:

nested_dict = {'dictA': {'key_1': 'value_1', 'key_1A': 'value_1A','key_1Asub1': {'Asub1': 'Asub1_val', 'sub_subA1': {'sub_subA1_key':'sub_subA1_val'}}},
                'dictB': {'key_2': 'value_2'},
                1: {'key_3': 'value_3', 'key_3A': 'value_3A'}}

def print_dict(dictionary):
    dictionary_array = [dictionary]
    for sub_dictionary in dictionary_array:
        if type(sub_dictionary) is dict:
            for key, value in sub_dictionary.items():
                print("key=", key)
                print("value", value)
                if type(value) is dict:
                    dictionary_array.append(value)



print_dict(nested_dict)

Nested dictionaries looping using isinstance() and yield function.使用 isinstance() 和 yield 函数循环嵌套字典。 **isinstance is afunction that returns the given input and reference is true or false as in below case dict is true so it go for iteration. **isinstance 是一个函数,它返回给定的输入,并且引用是 true 或 false,因为在下面的情况下,dict 是 true,所以它会进行迭代。 **Yield is used to return from a function without destroying the states of its local variable and when the function is called, the execution starts from the last yield statement. **yield 用于从函数返回而不破坏其局部变量的状态,并且在调用函数时,从最后一个 yield 语句开始执行。 Any function that contains a yield keyword is termed a generator.任何包含 yield 关键字的函数都称为生成器。

students= {'emp1': {'name': 'Bob', 'job': 'Mgr'},
     'emp2': {'name': 'Kim', 'job': 'Dev','emp3': {'namee': 'Saam', 'j0ob': 'Deev'}},
     'emp4': {'name': 'Sam', 'job': 'Dev'}}
def nested_dict_pairs_iterator(dict_obj):
     for key, value in dict_obj.items():
        # Check if value is of dict type
        if isinstance(value, dict):
            # If value is dict then iterate over all its values
            for pair in  nested_dict_pairs_iterator(value):
                yield (key, *pair)
        else:
            # If value is not dict type then yield the value
            yield (key, value)
for pair in nested_dict_pairs_iterator(students):
    print(pair)

You can print recursively with a dictionary comprehension:您可以使用字典理解递归打印:

def print_key_pairs(d):
    {k: print_key_pairs(v) if isinstance(v, dict) else print(f'{k}: {v}') for k, v in d.items()}

For your test case this is the output:对于您的测试用例,这是输出:

>>> print_key_pairs({u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}})
status: good
target: 1
port: 11

For a ready-made solution install ndicts对于现成的解决方案,请安装ndicts

pip install ndicts

Import a NestedDict in your script在脚本中导入 NestedDict

from ndicts.ndicts import NestedDict

Initialize初始化

dictionary = {
    u'xml': {
        u'config': {
            u'portstatus': {u'status': u'good'}, 
            u'target': u'1'
        },
    u'port': u'11'
    }
}

nd = NestedDict(dictionary)

Iterate迭代

for key, value in nd.items():
    print(key, value)

While the original solution from @Scharron is beautiful and simple, it cannot handle the list very well:虽然@Scharron 的原始解决方案既美观又简单,但它不能很好地处理列表:

def myprint(d):
    for k, v in d.items():
        if isinstance(v, dict):
            myprint(v)
        else:
            print("{0} : {1}".format(k, v))

So this code can be slightly modified like this to handle list in elements:因此,可以像这样稍微修改此代码以处理元素中的列表:

def myprint(d):
    for k, v in d.items():
        if isinstance(v, dict):
            myprint(v)
        elif isinstance(v, list):
            for i in v:
                myprint(i)
        else:
            print("{0} : {1}".format(k, v))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM