सभी नेस्टेड शब्दकोश मूल्यों के माध्यम से लूप?


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

मैं एक शब्दकोश के माध्यम से लूप करने की कोशिश कर रहा हूं और सभी प्रमुख मूल्य जोड़े को प्रिंट करता हूं जहां मूल्य एक नेस्टेड शब्दकोश नहीं है। यदि मूल्य एक शब्दकोष है तो मैं उसमें जाना चाहता हूं और उसके प्रमुख मूल्य जोड़े ... आदि का प्रिंट निकालता हूं। कोई मदद?

संपादित करें

इस बारे में कैसा है? यह अभी भी केवल एक चीज को प्रिंट करता है।

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
लगता है कि आप पुनरावृत्ति चाहते हैं, लेकिन यह विवरण स्पष्ट नहीं है कि यह सुनिश्चित हो। में कुछ उदाहरण के बारे में क्या / उत्पादन? इसके अलावा, आपके कोड में क्या गलत है?
निकल्स बी

2
पायथन में एक निश्चित पुनरावर्तन सीमा है: docs.python.org/library/sys.html#sys.setrecursionlimit
डॉ। जन-फिलिप गेहरके

2
@ Jan-PhilipGehrcke: पुनरावर्तन के बिना पेड़ जैसी डेटा संरचना पर एल्गोरिदम को लागू करना सादा आत्महत्या है।
निकल्स बी

2
@ तक्कुन: आप dictएक चर नाम के रूप में उपयोग कर रहे हैं । ऐसा कभी न करें (यह इसीलिए विफल है)।
निकोलस बी।

3
@NiklasB।, पुन: "आत्महत्या": मैंने अभी-अभी स्क्रहरन के एल्गोरिथ्म के एक पुनरावृत्त संस्करण को लागू किया है और इसकी सिर्फ दो पंक्तियों को लंबा और अभी भी काफी आसान है। इसके अलावा, पेड़ों से सामान्य रेखांकन पर जाने के दौरान पुनरावृत्ति का अनुवाद करना अक्सर एक आवश्यकता होती है।
फ्रेड फू

जवाबों:


157

जैसा कि निकलास ने कहा, आपको पुनरावृत्ति की आवश्यकता है, अर्थात आप अपने तानाशाही को मुद्रित करने के लिए एक फ़ंक्शन को परिभाषित करना चाहते हैं, और यदि मूल्य एक तानाशाही है, तो आप इस नए तानाशाह का उपयोग करके अपने प्रिंट फ़ंक्शन को कॉल करना चाहते हैं।

कुछ इस तरह :

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) कॉल करने से पहले प्रिंट (k) जोड़ें।
नाओमी फ्रिडमैन

पुनरावृत्ति के साथ यह तुच्छ है।
सर्जक

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 key1 से शब्दकोश में आता है , key2.3 पूरे शब्दकोश (पीछे के किनारे / चक्र) को इंगित करता है। जब एक बैक एज (चक्र) होता है, तो स्टैक / रिकर्सन असीम रूप से चलेगा।

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

यदि आप इस डिक्शनरी को स्क्रहरन से लागू करते हैं

    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

वही प्रेषक से कार्यान्वयन के साथ जाता है ।

इसी तरह, आपको फ्रेड फू के कार्यान्वयन से अनंत लूप मिलता है :

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

हालांकि, पायथन वास्तव में नेस्टेड शब्दकोश में चक्र का पता लगाता है:

    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)
करूंगा

1
इस पर ध्यान दिलाने के लिए धन्यवाद। क्या आप उत्तर में अपने कोड को शामिल करेंगे। मुझे लगता है कि यह आपके उत्कृष्ट उत्तर को पूरा करता है।
मूद्रा

Python3 के लिए, रिटर्न के list(d.items())रूप में उपयोग करें d.items(), एक सूची नहीं, और v.items()इसके बजाय का उपयोग करेंv.iteritems()
Max

33

चूँकि एक dictपुनरावृत्त है, आप केवल छोटे बदलावों के साथ इस समस्या के लिए क्लासिक नेस्टेड कंटेनर चलने योग्य सूत्र को लागू कर सकते हैं। यहाँ एक पायथन 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)]

पायथन 2 में, एक ऐसा रिवाज बनाना संभव हो सकता है Mappingजो योग्य Mappingहो iteritems, लेकिन इसमें शामिल नहीं है , इस मामले में यह विफल हो जाएगा। डॉक्स इंगित नहीं करता है कि iteritemsएक के लिए आवश्यक है Mapping; दूसरी ओर, स्रोतMapping प्रकार को एक iteritemsविधि देता है । तो कस्टम के लिए Mappings, collections.Mappingस्पष्ट रूप से सिर्फ मामले में विरासत में मिला ।

पायथन 3 में, कई सुधार किए जाने हैं। पायथन 3.3 के रूप में, अमूर्त आधार वर्ग में रहते हैं collections.abc। वे collectionsपीछे की संगतता के लिए भी बने हुए हैं, लेकिन यह एक नाम स्थान में एक साथ हमारे अमूर्त आधार वर्गों के लिए अच्छा है। तो इससे आयात abcहोता है collections। पायथन 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बेहतर है।
फ्रेड फू

1
@ लार्समैन, आप बिल्कुल सही कह रहे हैं। मैं सोच रहा था कि Iterableइस समाधान का उपयोग अधिक सामान्य हो जाएगा, यह भूलकर, जाहिर है, पुनरावृत्तियों जरूरी नहीं है iteritems
प्रेषिति

इस उत्तर के लिए +1 क्योंकि इसका एक सामान्य समाधान जो इस समस्या के लिए काम करता है, लेकिन यह केवल मूल्यों को छापने तक सीमित नहीं है। @ टटकुन आपको इस विकल्प पर विचार करना चाहिए। लंबे समय में आप मूल्यों को प्रिंट करने से अधिक चाहते हैं।
अलेजांद्रो पियाड

1
@ Seanny123, इस पर मेरा ध्यान आकर्षित करने के लिए धन्यवाद। अजगर 3 तस्वीर को कुछ तरीकों से बदलता है, वास्तव में - मैं इसे एक ऐसे संस्करण के रूप में फिर से लिखने जा रहा हूं जो नए yield fromवाक्यविन्यास का उपयोग करता है ।
12

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
हाँ, यह है कि मैं इसे देखने के लिए कैसे कल्पना की। धन्यवाद। तो इसका फायदा यह है कि यह बहुत गहरी घोंसले के लिए ढेर को नहीं बहाएगा? या इसमें कुछ और भी है?
निकल्स बी

@NiklasB .: हाँ, यह पहला लाभ है। इसके अलावा, इस संस्करण को स्टैक (ए list) dequeया यहां तक ​​कि प्राथमिकता कतार द्वारा प्रतिस्थापित करके आसानी से अलग-अलग ट्रैवर्सल ऑर्डर के लिए अनुकूलित किया जा सकता है ।
फ्रेड फू

हां, समझ में आता है। धन्यवाद और खुश कोडिंग :)
निकल्स बी

हाँ, लेकिन यह समाधान खदान और पुनरावर्ती की तुलना में अधिक जगह लेने वाला है।
schlamar

1
@ ms4py: मनोरंजन के लिए, मैंने एक बेंचमार्क बनाया । मेरे कंप्यूटर पर, पुनरावर्ती संस्करण सबसे तेज़ है और लार्समैन सभी तीन परीक्षण शब्दकोशों के लिए दूसरे स्थान पर है। जेनरेटर का उपयोग करने वाला संस्करण अपेक्षाकृत धीमा है, जैसा कि अपेक्षित था (क्योंकि इसमें विभिन्न जनरेटर संदर्भों के साथ बहुत कुछ करना है)
निकल्स बी

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'

यदि आप इसे इस तरह की आवश्यकता है, तो एक स्ट्रिंग के बजाय कुंजी के टपल के रूप में उपसर्ग को ट्रैक करने के लिए इसे संशोधित करना भी आसान है।


सूची में आउटपुट कैसे जोड़ें?
शश जूल 16'18

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

3

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

एक विकल्प के रूप में Iterative समाधान:

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

यह कैसा है? बिग ओ एक ही होना चाहिए (यह O(depth)पुनरावर्ती समाधान के लिए है। वही इस संस्करण पर लागू होता है, अगर मैं सही तरीके से सोच रहा हूं)।
निकल्स बी

"स्टैक कॉपी करें"? तुम्हारी किस बारे में बोलने की इच्छा थी? प्रत्येक फ़ंक्शन कॉल एक नया स्टैकफ़्रेम बनाता है। आपका समाधान itersएक स्पष्ट स्टैक के रूप में उपयोग करता है , इसलिए बिग-ओ मेमोरी खपत समान है, या मैं कुछ याद कर रहा हूं?
निकल्स बी

@NiklasB। रिकर्सियन हमेशा ओवरहेड के साथ आता है, विकिपीडिया पर इस खंड को विवरण के लिए देखें: en.wikipedia.org/wiki/… पुनरावर्ती समाधान का ढेर फ्रेम बहुत बड़ा है।
schlamar

आपको उस पैराग्राफ को गलत समझना चाहिए। यह आपके बयानों का समर्थन करने के लिए कुछ भी नहीं कहता है।
निकल्स बी

1
@NiklasB। नहीं, क्योंकि स्टैक फ्रेम यहाँ केवल पुनरावृत्ति है और पुनरावर्ती समाधान के लिए स्टैक फ्रेम में पुनरावृत्ति, प्रोग्राम काउंटर, चर वातावरण, आदि है ...
schlamar

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/… । क्या शब्दकोश की सूची के अंतिम तत्व को खोजने का एक तरीका है, इसे हटा दें और फिर एक स्तर ऊपर ले जाएं? यदि नहीं हटाया गया, तो मैं एक सूची बनाना चाहता हूं, जहां अंतिम तत्व डेटा की गहराई है इसलिए मैं सूची को हटा देता
हूं

1

यहां पायथन 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]])

तीन स्तरीय शब्दकोश के लिए।

आपको कई कुंजियों का उपयोग करने से पहले स्तरों की संख्या जानने की आवश्यकता है और स्तरों की संख्या स्थिर होनी चाहिए (मूल्यों के माध्यम से पुनरावृत्ति करते समय घोंसले के स्तर की संख्या की जांच करने के लिए स्क्रिप्ट का एक छोटा सा जोड़ना संभव हो सकता है, लेकिन मैंने नहीं किया है अभी तक इस पर देखा)।


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

मैंने पायथन 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)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.