すべてのネストされた辞書値をループしますか?


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

辞書をループして、値がネストされた辞書ではないすべてのキーと値のペアを出力しようとしています。値がディクショナリである場合は、それにアクセスして、そのキーと値のペアなどを出力します。何か助け?

編集

これはどう?それでも、1つのものだけを印刷します。

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

完全なテストケース

辞書:

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

結果:

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

1
再帰が必要なように聞こえますが、説明は十分に明確ではありません。入出力の例についてはどうですか?また、コードの何が問題になっていますか?
Niklas B.

2
Pythonには固定された再帰制限があります:docs.python.org/library/sys.html#sys.setrecursionlimit
Dr. Jan-Philip Gehrcke

2
@ Jan-PhilipGehrcke:再帰なしでツリーのようなデータ構造にアルゴリズムを実装することは自殺です。
Niklas B.

2
@Takkun:dict変数名として使用しています。これを行わないでください(これが失敗する理由です)。
Niklas B.

3
@NiklasB。、re: "suicide":私はScharronのアルゴリズムの反復バージョンを実装しただけで、その2行だけ長く、非常に簡単に追跡できます。さらに、再帰を反復に変換することは、多くの場合、ツリーから一般的なグラフに移行するときに必要です。
Fred Foo

回答:


156

Niklasが言ったように、再帰が必要です。つまり、dictを出力する関数を定義し、値がdictの場合は、この新しいdictを使用してprint関数を呼び出します。

何かのようなもの :

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

3
小さな改善。myprint(v)を呼び出す前に、print(k)を追加します。
Naomi Fridman

再帰があればそれは簡単です。
sergzach

36

独自の再帰的実装またはスタックを使用した同等の反復を作成する場合、潜在的な問題があります。この例を見てください:

    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

通常の意味では、入れ子になった辞書はデータ構造のようなn-naryツリーになります。しかし、この定義、クロスエッジまたはバックエッジ(したがって、もはやツリーではない)の可能性を排除しいません。例えば、ここでkey2.2から辞書に保持KEY1key2.3の全体の辞書を指し(バックエッジ/サイクル)。バックエッジ(サイクル)がある場合、スタック/再帰は無限に実行されます。

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

Scharronからのこの実装でこの辞書を印刷する場合

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

次のエラーが表示されます。

    RuntimeError: maximum recursion depth exceeded while calling a Python object

sendleからの実装についても同様です。

同様に、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))

ただし、Pythonは実際にはネストされた辞書でサイクルを検出します。

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

「{...}」は、サイクルが検出される場所です。

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)

それでは、反復ソリューションをどのように実装しますか?
dreftymac

2
キーはサイクルを行く避けるためにため@dreftymac私が訪問したセットを追加します。def myprint(d): stack = d.items() visited = set() while stack: k, v = stack.pop() if isinstance(v, dict): if k not in visited: stack.extend(v.iteritems()) else: print("%s: %s" % (k, v)) visited.add(k)
tengr

1
これを指摘してくれてありがとう。答えにコードを含めていただけませんか。それはあなたの素晴らしい答えを完成させると思います。
Moondra

python3のために、使用しlist(d.items())d.items()戻っビューではなく、リストを、そして使用v.items()するのではなくv.iteritems()
マックス

33

a dictは反復可能であるため、わずかな変更をいくつか加えるだけで、この問題に古典的なネストされたコンテナの反復可能数式を適用できます。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

テスト:

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)]

Pythonの2では、可能性があるカスタム作成することが可能Mappingとして適格であることMappingが、が含まれていませんiteritems、その場合には、これは失敗しますが。ドキュメントiteritemsは、に必要であることを示していませんMapping。一方、ソースMapping型にiteritemsメソッドを提供します。したがって、カスタムのMappings場合は、念のためcollections.Mapping明示的に継承します。

Python 3では、いくつかの改善が行われています。Python 3.3以降、抽象基本クラスはにありcollections.abcます。これらはcollections下位互換性のために残されていますが、1つの名前空間に抽象基本クラスをまとめた方が便利です。したがって、これはabcからインポートされcollectionsます。Python 3.3ではも追加yield fromされます。これは、このような状況に対応するように設計されています。これは空の構文糖ではありません。これにより、コード高速になりコルーチンとのより適切なやり取りが可能になります

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

3
isinstance(item, collections.Iterable)の保証はありませんhasattr(item, "iteritems")。確認することをお勧めしcollections.Mappingます。
Fred Foo、2012年

1
@larsmansさん、もちろんあなたの言うとおりです。を使用Iterableすると、このソリューションがより一般化されると考えていましたが、明らかに、反復可能オブジェクトには必ずしも必要ではないことを忘れていましたiteritems
sendle

この答えに+1するのは、この問題に対して機能する一般的な解決策ですが、値を出力するだけに限定されないためです。@Takkunでは、このオプションを必ず検討してください。長期的には、値を出力するだけでは不十分です。
アレハンドロピアド

1
@ Seanny123、これに私の注意を引いてくれてありがとう。実際、Python 3はいくつかの点で状況を変えています-これを、新しいyield from構文を使用するバージョンとして書き直します。
センダーレ2017

25

代替の反復ソリューション:

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

2
ええ、それは私がそれがどのように見えるかを想像した方法です。ありがとう。これの利点は、非常に深い入れ子のスタックがオーバーフローしないことです。それとも他に何かありますか?
Niklas B.

@NiklasB .:うん、それが最初のメリットだ。また、このバージョンは、スタック(a list)をdeque優先度キューに置き換えることで、さまざまなトラバーサル順序に非常に簡単に適応できます。
Fred Foo、2012年

うん、理にかなっている。ありがとう、そしてコーディングをありがとう:)
Niklas B.

はい、しかし、このソリューションは私のソリューションと再帰的なソリューションよりも多くのスペースを消費します。
シュラマー

1
@ ms4py:楽しみのために、ベンチマークを作成しまし。私のコンピューターでは、再帰バージョンが最も速く、ラーズマンは3つのテスト辞書すべてで2番目です。ジェネレーターを使用するバージョンは、予想どおり、比較的低速です(さまざまなジェネレーターコンテキストで多くのジャグリングを行う必要があるため)
NiklasB。

9

私が書いたわずかに異なるバージョンは、そこに到達するまでの過程でキーを追跡します

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

あなたのデータでは、それは印刷されます

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

必要に応じて、文字列ではなくキーのタプルとしてプレフィックスを追跡するように変更することも簡単です。


リストに出力を追加する方法は?
Shash 2018

5

これを行うpythonicの方法を次に示します。この関数を使用すると、すべてのレベルでキーと値のペアをループできます。それはすべてをメモリに保存するのではなく、あなたがそれをループするときに辞書を歩く

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)

プリント

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

2

代替策としての反復ソリューション:

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

どう?Big Oは同じである必要があります(これO(depth)は再帰的な解決策のためのものです。私が正しく考えていれば、このバージョンにも同じことが当てはまります)。
Niklas B.

「スタックをコピー」?あなたは何について話していますか?関数を呼び出すたびに、新しいスタックフレームが作成されます。あなたのソリューションはiters明示的なスタックとして使用するので、Big-Oのメモリ消費は同じですか、それとも何か不足していますか?
Niklas B.

@NiklasB。再帰には常にオーバーヘッドが伴います。詳細については、Wikipediaのこのセクションを参照してください:en.wikipedia.org/wiki/…再帰的ソリューションのスタックフレームははるかに大きくなります。
シュラマー

あなたはその段落を誤解しているに違いありません。それはあなたの声明を支持するために何も言っていません。
Niklas B.

1
@NiklasB。ここでのスタックフレームのみITERや再帰的な解決のためにあるのでいいえ、スタックフレームはITER、プログラム・カウンタ、変数の環境、等...ている
schlamar

2

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)

2

次のコードを使用して、値がディクショナリを含むリストである可能性がある場所を考慮して、ネストされたディクショナリのすべての値を出力しています。これは、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)

出力:

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

私はここに似たような問題を抱えています。stackoverflow.com/ questions / 50642922 / … 。辞書のリストの最後の要素を見つけ、それを削除してからレベルを上げる方法はありますか?削除しない場合、最後の要素がデータの深さであるリストを作成したいので、リストを逆にして削除します
Heenashree Khandelwal

1

これは、Fred FooのPython 2に対する回答の修正バージョンです。元の応答では、最も深いレベルのネストのみが出力されます。キーをリストとして出力する場合、すべてのレベルのキーを保持できますが、キーを参照するには、リストのリストを参照する必要があります。

これが関数です:

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

キーを参照するには:

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

3レベルの辞書の場合。

複数のキーにアクセスする前にレベルの数を知っている必要があり、レベルの数は一定である必要があります(値を反復するときにネストするレベルの数をチェックするためにスクリプトを少し追加することは可能かもしれませんが、私はしていませんまだこれを見た)。


1

このアプローチは少し柔軟です。ここでは、キーと値のペアを生成し、リストを反復処理するように簡単に拡張できるジェネレータ関数を提供します。

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

次に、独自のmyprint関数を記述して、それらのキーと値のペアを出力します。

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

テスト:

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

出力:

status : good
target : 1
port : 11

これをPython 3.6でテストしました。


0

これらの回答は、2レベルのサブディクショナリでのみ機能します。詳細については、これを試してください:

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.