शब्दकोशों के शब्दकोश को कैसे मर्ज किया जाए?


129

मुझे कई शब्दकोशों को मर्ज करने की आवश्यकता है, यहाँ उदाहरण के लिए मेरे पास क्या है:

dict1 = {1:{"a":{A}}, 2:{"b":{B}}}

dict2 = {2:{"c":{C}}, 3:{"d":{D}}

साथ A B Cऔर Dपेड़, की पत्तियों की तरह किया जा रहा है{"info1":"value", "info2":"value2"}

शब्दकोशों का एक अज्ञात स्तर (गहराई) है, यह हो सकता है {2:{"c":{"z":{"y":{C}}}}}

मेरे मामले में यह एक निर्देशिका / फाइलों की संरचना का प्रतिनिधित्व करता है जिसमें नोड्स डॉक्स और पत्तियां फाइलें हैं।

मैं उन्हें प्राप्त करने के लिए मर्ज करना चाहता हूं:

 dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}

मुझे यकीन नहीं है कि मैं पायथन के साथ आसानी से कैसे कर सकता हूं।


आप शब्दकोशों की अपनी मनमानी गहराई के लिए क्या चाहते हैं? क्या आप स्तर yतक चपटे cया क्या चाहते हैं? आपका उदाहरण अधूरा है।
एजीएफ़

यहां मेरे NestedDict वर्ग की जाँच करें: stackoverflow.com/a/16296144/2334951 यह विलय और अधिक जैसे नेस्टेड शब्दकोश संरचनाओं का प्रबंधन करता है।
स्ज़िएबर्थअदम

3
समाधान की तलाश करने वाले सभी के लिए एक चेतावनी: यह सवाल केवल नेस्टेड डाइक के बारे में है। अधिकांश उत्तर संरचना के भीतर डक्ट्स की सूचियों के अधिक जटिल मामले को ठीक से नहीं संभालते हैं। यदि आपको इसकी आवश्यकता हो तो नीचे दिए गए @Oiloke के उत्तर को आज़माएं: stackoverflow.com/a/25270947/1431660
SHernandez

यह भी देखें: अजगर dpath मर्ज
dreftymac

यह भी देखें:
dreftymac

जवाबों:


143

यह वास्तव में काफी मुश्किल है - खासकर अगर आप चीजों को असंगत होने पर एक उपयोगी त्रुटि संदेश चाहते हैं, जबकि डुप्लिकेट लेकिन सुसंगत प्रविष्टियों को सही ढंग से स्वीकार करते हैं (यहां कोई अन्य उत्तर नहीं है ....)

यह मान लेना कि आपके पास बड़ी संख्या में प्रविष्टियाँ नहीं हैं एक पुनरावर्ती कार्य सबसे आसान है:

def merge(a, b, path=None):
    "merges b into a"
    if path is None: path = []
    for key in b:
        if key in a:
            if isinstance(a[key], dict) and isinstance(b[key], dict):
                merge(a[key], b[key], path + [str(key)])
            elif a[key] == b[key]:
                pass # same leaf value
            else:
                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
        else:
            a[key] = b[key]
    return a

# works
print(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}}))
# has conflict
merge({1:{"a":"A"},2:{"b":"B"}}, {1:{"a":"A"},2:{"b":"C"}})

ध्यान दें कि यह उत्परिवर्तन a- की सामग्री को bइसमें जोड़ा जाता है a(जो भी लौटा दिया गया है)। यदि आप रखना चाहते हैं तो आप aइसे पसंद कर सकते हैं merge(dict(a), b)

agf ने बताया (नीचे) कि आपके पास दो से अधिक डाइक हो सकते हैं, जिस स्थिति में आप उपयोग कर सकते हैं:

reduce(merge, [dict1, dict2, dict3...])

जहां सब कुछ dict1 में जोड़ा जाएगा।

[नोट - मैंने पहले तर्क को बदलने के लिए अपना प्रारंभिक उत्तर संपादित किया; यह समझाने के लिए "कम" करना आसान बनाता है]

ps में अजगर 3, आपको भी आवश्यकता होगी from functools import reduce


1
फिर आप इसे दो के बजाय एस के reduceएक मनमाने संख्या के साथ काम करने के लिए एक या बराबर लूप के अंदर चिपका सकते हैं dict। हालांकि, मुझे यकीन नहीं है कि यह वही करता है जो वह या तो चाहता है (वह स्पष्ट नहीं था), आप 2: {'c': {'z': {'y': {'info1': 'value', 'info2': 'value2'}}}, 'b': {'info1': 'value', 'info2': 'value2'}}अपने दूसरे उदाहरण के लिए समाप्त होते हैं , मुझे यकीन नहीं है कि वह चाहता है zऔर yचपटा हुआ है या नहीं?
एजीएफ़

1
वे निर्देशिका संरचनाएं हैं, इसलिए मुझे नहीं लगता कि वह कुछ भी चपटा चाहते हैं? ओह, क्षमा करें, "कई शब्दकोष" छूट गए। हां, कम करना अच्छा होगा। जोड़ देगा।
एंड्रयू कूके

यह वही है जो मैं चाहता था! मुझे खेद है कि मैं पर्याप्त स्पष्ट नहीं था ... मुझे लगा कि मैं पायथन के साथ ठीक था, ऐसा नहीं लगता: - / मुझे नेस्टेड डक्ट्स के कारण एक पुनरावर्ती कार्य की आवश्यकता थी, यह एक काम करता है और मैं इसे समझ सकता हूं :) मुझे नहीं लगता यह कम करने के साथ काम करने में सक्षम होने के लिए लग रहे हो ...
fdhex

2
Dicts के तहत अंतिम नेस्टेड स्तर के रूप में सूचियों वाले किसी भी व्यक्ति के लिए, आप दो सूचियों को प्राप्त करने के लिए त्रुटि को बढ़ाने के बजाय ऐसा कर सकते हैं a[key] = a[key] + b[key]:। उपयोगी उत्तर के लिए धन्यवाद।
केविनमीक

1
> यदि आप रखना चाहते हैं, तो आप इसे मर्ज (तानाशाही (क), ख) के रूप में कह सकते हैं कि नेस्टेड डाइक अभी भी म्यूट किए जाएंगे। इससे बचने के लिए उपयोग करें copy.deepcopy
6

30

यहां जनरेटर का उपयोग करने का एक आसान तरीका है:

def mergedicts(dict1, dict2):
    for k in set(dict1.keys()).union(dict2.keys()):
        if k in dict1 and k in dict2:
            if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
                yield (k, dict(mergedicts(dict1[k], dict2[k])))
            else:
                # If one of the values is not a dict, you can't continue merging it.
                # Value from second dict overrides one in first and we move on.
                yield (k, dict2[k])
                # Alternatively, replace this with exception raiser to alert you of value conflicts
        elif k in dict1:
            yield (k, dict1[k])
        else:
            yield (k, dict2[k])

dict1 = {1:{"a":"A"},2:{"b":"B"}}
dict2 = {2:{"c":"C"},3:{"d":"D"}}

print dict(mergedicts(dict1,dict2))

यह प्रिंट:

{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}

यदि आप जेनरेटर विषय को रखना चाहते हैं तो आप चेन (तानाशाही 1.keys), तानाशाही 2.keys ())
andrew Cooke

क्या डुप्लिकेट कुंजी नहीं मिलेगी?
jterrace

यह काम करने के लिए लगता है, कम से कम मेरे डेटा के सेट पर, लेकिन जैसा कि मैंने पैदावार और जनरेटर को अच्छी तरह से कभी नहीं समझा, मैं बहुत ज्यादा क्यों हार गया हूं, लेकिन मैं थोड़ा कठिन प्रयास करने वाला हूं, उपयोगी हो सकता है!
fdhex

आह, हाँ, यह डुप्लिकेट चाबियाँ मिल जाएगा। आपको अभी भी इसे एक सेट में लपेटने की आवश्यकता है, क्षमा करें।
andrew कुकी

2
मुझे यह विशेष रूप से मददगार लगा। लेकिन फंक्शनिंग को फंक्शन को पैरामीटर के रूप में हल करने देना होगा।
मेंटैग

25

इस सवाल के साथ एक मुद्दा यह है कि तानाशाह के मूल्यों में डेटा के मनमाने ढंग से जटिल टुकड़े हो सकते हैं। इन और अन्य उत्तरों के आधार पर मैं इस कोड के साथ आया:

class YamlReaderError(Exception):
    pass

def data_merge(a, b):
    """merges b into a and return merged result

    NOTE: tuples and arbitrary objects are not handled as it is totally ambiguous what should happen"""
    key = None
    # ## debug output
    # sys.stderr.write("DEBUG: %s to %s\n" %(b,a))
    try:
        if a is None or isinstance(a, str) or isinstance(a, unicode) or isinstance(a, int) or isinstance(a, long) or isinstance(a, float):
            # border case for first run or if a is a primitive
            a = b
        elif isinstance(a, list):
            # lists can be only appended
            if isinstance(b, list):
                # merge lists
                a.extend(b)
            else:
                # append to list
                a.append(b)
        elif isinstance(a, dict):
            # dicts must be merged
            if isinstance(b, dict):
                for key in b:
                    if key in a:
                        a[key] = data_merge(a[key], b[key])
                    else:
                        a[key] = b[key]
            else:
                raise YamlReaderError('Cannot merge non-dict "%s" into dict "%s"' % (b, a))
        else:
            raise YamlReaderError('NOT IMPLEMENTED "%s" into "%s"' % (b, a))
    except TypeError, e:
        raise YamlReaderError('TypeError "%s" in key "%s" when merging "%s" into "%s"' % (e, key, b, a))
    return a

मेरा उपयोग मामला YAML फ़ाइलों को मर्ज कर रहा है जहां मुझे केवल संभव डेटा प्रकारों के सबसेट से निपटना है। इसलिए मैं ट्यूल और अन्य वस्तुओं को अनदेखा कर सकता हूं। मेरे लिए एक समझदार मर्ज लॉजिक का मतलब है

  • खोपड़ी की जगह
  • सूची में जोड़ें
  • गुम कुंजियाँ जोड़कर और मौजूदा कुंजियों को अपडेट करके dicts को मर्ज करें

बाकी सब और अप्रत्याशित परिणाम एक त्रुटि है।


1
बहुत खुबस। जसन डंप पर भी अच्छी तरह से काम करता है। बस त्रुटि से निपटने को हटा दिया। (आलसी होने के नाते, मुझे यकीन है कि json के लिए उचित काम कर सकते हैं)
dgBP

3
"आइंस्टीन" अनुक्रम को w / isinstance(a, (str, unicode, int, long, float))isnt से बदला जा सकता है?
सिमहाक

12

शब्दकोशों के शब्दकोश विलय

जैसा कि यह विहित प्रश्न है (कुछ गैर-सामान्यताओं के बावजूद) मैं इस मुद्दे को हल करने के लिए विहित पाइथोनिक दृष्टिकोण प्रदान कर रहा हूं।

सबसे सरल मामला: "पत्तियों में घोंसले के डक्ट होते हैं जो खाली डिकोट्स में समाप्त होते हैं":

d1 = {'a': {1: {'foo': {}}, 2: {}}}
d2 = {'a': {1: {}, 2: {'bar': {}}}}
d3 = {'b': {3: {'baz': {}}}}
d4 = {'a': {1: {'quux': {}}}}

यह पुनरावृत्ति के लिए सबसे सरल मामला है, और मैं दो भोले दृष्टिकोणों की सिफारिश करूंगा:

def rec_merge1(d1, d2):
    '''return new merged dict of dicts'''
    for k, v in d1.items(): # in Python 2, use .iteritems()!
        if k in d2:
            d2[k] = rec_merge1(v, d2[k])
    d3 = d1.copy()
    d3.update(d2)
    return d3

def rec_merge2(d1, d2):
    '''update first dict with second recursively'''
    for k, v in d1.items(): # in Python 2, use .iteritems()!
        if k in d2:
            d2[k] = rec_merge2(v, d2[k])
    d1.update(d2)
    return d1

मुझे विश्वास है कि मैं पहले से दूसरे को पसंद करूंगा, लेकिन ध्यान रखें कि पहले की मूल स्थिति को उसके मूल से फिर से बनाना होगा। यहाँ उपयोग है:

>>> from functools import reduce # only required for Python 3.
>>> reduce(rec_merge1, (d1, d2, d3, d4))
{'a': {1: {'quux': {}, 'foo': {}}, 2: {'bar': {}}}, 'b': {3: {'baz': {}}}}
>>> reduce(rec_merge2, (d1, d2, d3, d4))
{'a': {1: {'quux': {}, 'foo': {}}, 2: {'bar': {}}}, 'b': {3: {'baz': {}}}}

जटिल मामला: "पत्तियां किसी अन्य प्रकार की होती हैं:"

इसलिए यदि वे डिक्ट्स में समाप्त हो जाते हैं, तो यह अंतिम खाली डिकट्स को मर्ज करने का एक सरल मामला है। यदि नहीं, तो यह बहुत तुच्छ नहीं है। यदि तार, आप उन्हें कैसे मिलाते हैं? सेट को समान रूप से अपडेट किया जा सकता है, इसलिए हम उस उपचार को दे सकते हैं, लेकिन हम उस क्रम को खो देते हैं जिसमें वे विलय किए गए थे। तो आदेश देता है?

तो अधिक जानकारी के एवज में, सबसे सरल तरीका यह होगा कि अगर दोनों मान डिक्ट न हों तो उन्हें स्टैण्डर्ड अपडेट ट्रीटमेंट देना होगा: यानी दूसरे डिक्टेंड की वैल्यू पहले को ओवरराइट कर देगी, भले ही दूसरी डिक्टेट की वैल्यू कोई भी हो और पहली की वैल्यू एक ही हो। बहुत सारी जानकारी के साथ तानाशाह।

d1 = {'a': {1: 'foo', 2: None}}
d2 = {'a': {1: None, 2: 'bar'}}
d3 = {'b': {3: 'baz'}}
d4 = {'a': {1: 'quux'}}

from collections import MutableMapping

def rec_merge(d1, d2):
    '''
    Update two dicts of dicts recursively, 
    if either mapping has leaves that are non-dicts, 
    the second's leaf overwrites the first's.
    '''
    for k, v in d1.items(): # in Python 2, use .iteritems()!
        if k in d2:
            # this next check is the only difference!
            if all(isinstance(e, MutableMapping) for e in (v, d2[k])):
                d2[k] = rec_merge(v, d2[k])
            # we could further check types and merge as appropriate here.
    d3 = d1.copy()
    d3.update(d2)
    return d3

और अब

from functools import reduce
reduce(rec_merge, (d1, d2, d3, d4))

रिटर्न

{'a': {1: 'quux', 2: 'bar'}, 'b': {3: 'baz'}}

मूल प्रश्न के लिए आवेदन:

मुझे अक्षरों के चारों ओर घुंघराले ब्रेसिज़ को निकालना है और उन्हें वैध पायथन होने के लिए एकल उद्धरणों में डाल दिया है (अन्यथा वे पायथन 2.7+ में शाब्दिक रूप से सेट होंगे) और साथ ही एक लापता ब्रेस संलग्न करें:

dict1 = {1:{"a":'A'}, 2:{"b":'B'}}
dict2 = {2:{"c":'C'}, 3:{"d":'D'}}

और rec_merge(dict1, dict2)अब लौटता है:

{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}

कौन सा मूल प्रश्न के वांछित परिणाम (परिवर्तित करने के बाद, उदाहरण के लिए मेल खाता है {A}करने के लिए 'A'।)


10

@Andrew cooke पर आधारित है। यह संस्करण dicts की नेस्टेड सूचियों को संभालता है और विकल्प को मूल्यों को अपडेट करने की भी अनुमति देता है

def merge(a, b, path=None, update=True):
    "http://stackoverflow.com/questions/7204805/python-dictionaries-of-dictionaries-merge"
    "merges b into a"
    if path is None: path = []
    for key in b:
        if key in a:
            if isinstance(a[key], dict) and isinstance(b[key], dict):
                merge(a[key], b[key], path + [str(key)])
            elif a[key] == b[key]:
                pass # same leaf value
            elif isinstance(a[key], list) and isinstance(b[key], list):
                for idx, val in enumerate(b[key]):
                    a[key][idx] = merge(a[key][idx], b[key][idx], path + [str(key), str(idx)], update=update)
            elif update:
                a[key] = b[key]
            else:
                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
        else:
            a[key] = b[key]
    return a

1
धन्यवाद, यह इतना उपयोगी है। मैं हर समय अपनी संरचनाओं में dicts की सूची रहा हूँ, अन्य समाधान इसे ठीक से विलय नहीं कर सकते।
शेरनदेज़ सिप

7

यह सरल पुनरावर्ती प्रक्रिया परस्पर विरोधी कुंजियों को पार करते हुए एक शब्दकोश को दूसरे में विलय कर देगी:

#!/usr/bin/env python2.7

def merge_dicts(dict1, dict2):
    """ Recursively merges dict2 into dict1 """
    if not isinstance(dict1, dict) or not isinstance(dict2, dict):
        return dict2
    for k in dict2:
        if k in dict1:
            dict1[k] = merge_dicts(dict1[k], dict2[k])
        else:
            dict1[k] = dict2[k]
    return dict1

print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {2:{"c":"C"}, 3:{"d":"D"}}))
print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {1:{"a":"A"}, 2:{"b":"C"}}))

आउटपुट:

{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}
{1: {'a': 'A'}, 2: {'b': 'C'}}

7

@Andrew cooke के जवाबों के आधार पर। यह बेहतर तरीके से नेस्टेड सूचियों का ध्यान रखता है।

def deep_merge_lists(original, incoming):
    """
    Deep merge two lists. Modifies original.
    Recursively call deep merge on each correlated element of list. 
    If item type in both elements are
     a. dict: Call deep_merge_dicts on both values.
     b. list: Recursively call deep_merge_lists on both values.
     c. any other type: Value is overridden.
     d. conflicting types: Value is overridden.

    If length of incoming list is more that of original then extra values are appended.
    """
    common_length = min(len(original), len(incoming))
    for idx in range(common_length):
        if isinstance(original[idx], dict) and isinstance(incoming[idx], dict):
            deep_merge_dicts(original[idx], incoming[idx])

        elif isinstance(original[idx], list) and isinstance(incoming[idx], list):
            deep_merge_lists(original[idx], incoming[idx])

        else:
            original[idx] = incoming[idx]

    for idx in range(common_length, len(incoming)):
        original.append(incoming[idx])


def deep_merge_dicts(original, incoming):
    """
    Deep merge two dictionaries. Modifies original.
    For key conflicts if both values are:
     a. dict: Recursively call deep_merge_dicts on both values.
     b. list: Call deep_merge_lists on both values.
     c. any other type: Value is overridden.
     d. conflicting types: Value is overridden.

    """
    for key in incoming:
        if key in original:
            if isinstance(original[key], dict) and isinstance(incoming[key], dict):
                deep_merge_dicts(original[key], incoming[key])

            elif isinstance(original[key], list) and isinstance(incoming[key], list):
                deep_merge_lists(original[key], incoming[key])

            else:
                original[key] = incoming[key]
        else:
            original[key] = incoming[key]

सहज और सममित। +1 सूची हैंडलिंग के लिए :)
vdwees

6

यदि आपके पास अज्ञात स्तर के शब्दकोश हैं, तो मैं एक पुनरावर्ती कार्य का सुझाव दूंगा:

def combineDicts(dictionary1, dictionary2):
    output = {}
    for item, value in dictionary1.iteritems():
        if dictionary2.has_key(item):
            if isinstance(dictionary2[item], dict):
                output[item] = combineDicts(value, dictionary2.pop(item))
        else:
            output[item] = value
    for item, value in dictionary2.iteritems():
         output[item] = value
    return output

5

अवलोकन

निम्नलिखित दृष्टिकोण में dicts के गहरे मर्ज की समस्या को उपविभाजित करता है:

  1. एक मानकीकृत उथले मर्ज फ़ंक्शन merge(f)(a,b)जो fदो डाइक को मर्ज करने के लिए फ़ंक्शन का उपयोग करता है aऔरb

  2. एक पुनरावर्ती विलय समारोह के fसाथ प्रयोग किया जाना हैmerge


कार्यान्वयन

दो (नॉन नेस्टेड) ​​डाइकस को मिलाने का एक फ़ंक्शन बहुत तरीकों से लिखा जा सकता है। मुझे व्यक्तिगत रूप से पसंद है

def merge(f):
    def merge(a,b): 
        keys = a.keys() | b.keys()
        return {key:f(a.get(key), b.get(key)) for key in keys}
    return merge

एक उपयुक्त पुनरावर्ती विलय समारोह fको परिभाषित करने का एक अच्छा तरीका गुणक का उपयोग कर रहा है जो उन कार्यों को परिभाषित करने की अनुमति देता है जो उनके तर्कों के प्रकार के आधार पर विभिन्न रास्तों के साथ मूल्यांकन करते हैं।

from multipledispatch import dispatch

#for anything that is not a dict return
@dispatch(object, object)
def f(a, b):
    return b if b is not None else a

#for dicts recurse 
@dispatch(dict, dict)
def f(a,b):
    return merge(f)(a,b)

उदाहरण

दो नेस्टेड डाइक को मर्ज करने के लिए बस merge(f)उदाहरण का उपयोग करें :

dict1 = {1:{"a":"A"},2:{"b":"B"}}
dict2 = {2:{"c":"C"},3:{"d":"D"}}
merge(f)(dict1, dict2)
#returns {1: {'a': 'A'}, 2: {'b': 'B', 'c': 'C'}, 3: {'d': 'D'}} 

टिप्पणियाँ:

इस दृष्टिकोण के लाभ हैं:

  • फ़ंक्शन छोटे कार्यों से निर्मित होता है जो प्रत्येक एक एकल कार्य करते हैं जो कोड को कारण और परीक्षण के लिए सरल बनाता है

  • व्यवहार हार्ड-कोडेड नहीं है, लेकिन कोड के पुन: उपयोग को बेहतर बनाने वाले आवश्यकतानुसार परिवर्तित और विस्तारित किया जा सकता है (नीचे उदाहरण देखें)।


अनुकूलन

कुछ उत्तरों ने उन dicts पर भी विचार किया, जिनमें अन्य (संभावित रूप से नेस्टेड) ​​dicts की सूचियाँ हैं। इस मामले में, कोई व्यक्ति सूचियों पर नक्शा चाहता है और स्थिति के आधार पर उन्हें मर्ज कर सकता है। इस विलय समारोह में एक और परिभाषा जोड़कर किया जा सकता है f:

import itertools
@dispatch(list, list)
def f(a,b):
    return [merge(f)(*arg) for arg in itertools.zip_longest(a, b)]

4

अगर कोई इस समस्या के लिए एक और दृष्टिकोण चाहता है, तो यहां मेरा समाधान है।

गुण : शैली में संक्षिप्त, घोषणात्मक और कार्यात्मक (पुनरावर्ती, कोई उत्परिवर्तन नहीं करता है)।

संभावित कमी : यह वह मर्ज नहीं हो सकता है जिसकी आपको तलाश है। शब्दार्थ के लिए डॉकट्रिंग से परामर्श करें।

def deep_merge(a, b):
    """
    Merge two values, with `b` taking precedence over `a`.

    Semantics:
    - If either `a` or `b` is not a dictionary, `a` will be returned only if
      `b` is `None`. Otherwise `b` will be returned.
    - If both values are dictionaries, they are merged as follows:
        * Each key that is found only in `a` or only in `b` will be included in
          the output collection with its value intact.
        * For any key in common between `a` and `b`, the corresponding values
          will be merged with the same semantics.
    """
    if not isinstance(a, dict) or not isinstance(b, dict):
        return a if b is None else b
    else:
        # If we're here, both a and b must be dictionaries or subtypes thereof.

        # Compute set of all keys in both dictionaries.
        keys = set(a.keys()) | set(b.keys())

        # Build output dictionary, merging recursively values with common keys,
        # where `None` is used to mean the absence of a value.
        return {
            key: deep_merge(a.get(key), b.get(key))
            for key in keys
        }

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


3

Andrew cookes उत्तर के साथ थोड़ी समस्या है: कुछ मामलों में यह दूसरे तर्क को संशोधित करता है bजब आप लौटे हुए हुक्म को संशोधित करते हैं। विशेष रूप से यह इस लाइन के कारण है:

if key in a:
    ...
else:
    a[key] = b[key]

यदि b[key]कोई है dict, तो इसे बस सौंपा जाएगा a, जिसका अर्थ है कि बाद के किसी भी संशोधन dictदोनों को प्रभावित करेगा aऔर b

a={}
b={'1':{'2':'b'}}
c={'1':{'3':'c'}}
merge(merge(a,b), c) # {'1': {'3': 'c', '2': 'b'}}
a # {'1': {'3': 'c', '2': 'b'}} (as expected)
b # {'1': {'3': 'c', '2': 'b'}} <----
c # {'1': {'3': 'c'}} (unmodified)

इसे ठीक करने के लिए, लाइन को इसके साथ प्रतिस्थापित करना होगा:

if isinstance(b[key], dict):
    a[key] = clone_dict(b[key])
else:
    a[key] = b[key]

कहाँ clone_dictहै:

def clone_dict(obj):
    clone = {}
    for key, value in obj.iteritems():
        if isinstance(value, dict):
            clone[key] = clone_dict(value)
        else:
            clone[key] = value
    return

फिर भी। यह स्पष्ट रूप से के लिए खाते में नहीं है list, setऔर अन्य सामग्री, लेकिन मुझे आशा है कि यह नुकसान दिखाता है जब मर्ज करने की कोशिश कर dicts

और पूर्णता के लिए, यहाँ मेरा संस्करण है, जहाँ आप इसे एकाधिक पास कर सकते हैं dicts:

def merge_dicts(*args):
    def clone_dict(obj):
        clone = {}
        for key, value in obj.iteritems():
            if isinstance(value, dict):
                clone[key] = clone_dict(value)
            else:
                clone[key] = value
        return

    def merge(a, b, path=[]):
        for key in b:
            if key in a:
                if isinstance(a[key], dict) and isinstance(b[key], dict):
                    merge(a[key], b[key], path + [str(key)])
                elif a[key] == b[key]:
                    pass
                else:
                    raise Exception('Conflict at `{path}\''.format(path='.'.join(path + [str(key)])))
            else:
                if isinstance(b[key], dict):
                    a[key] = clone_dict(b[key])
                else:
                    a[key] = b[key]
        return a
    return reduce(merge, args, {})

deepcopyइसके बजाय क्यों नहीं clone_dict?
आर्मंडो पेरेज़ मार्केज़

1
क्योंकि अजगर stdlib विशाल और शानदार है! मेरे पास इसका कोई सुराग नहीं था - प्लस यह कोड के लिए एक मजेदार छोटी सी बात थी :-)
और

2

फ़ंक्शन का यह संस्करण एन शब्दकोशों की संख्या के लिए जिम्मेदार होगा, और केवल शब्दकोष - कोई भी अनुचित पैरामीटर पारित नहीं किया जा सकता है, या यह एक टाइपर्रॉम को बढ़ाएगा। मर्ज स्वयं महत्वपूर्ण संघर्षों के लिए खाता है, और मर्ज श्रृंखला के नीचे एक शब्दकोश से डेटा को अधिलेखित करने के बजाय, यह मूल्यों का एक सेट बनाता है और उस पर निर्भर करता है; कोई डेटा नहीं खोया है।

यह पृष्ठ पर सबसे अधिक घातक नहीं हो सकता है, लेकिन यह सबसे अधिक गहन है और जब आप अपने 2 को एन dicts में मर्ज करते हैं तो आप किसी भी जानकारी को खोने वाले नहीं हैं।

def merge_dicts(*dicts):
    if not reduce(lambda x, y: isinstance(y, dict) and x, dicts, True):
        raise TypeError, "Object in *dicts not of type dict"
    if len(dicts) < 2:
        raise ValueError, "Requires 2 or more dict objects"


    def merge(a, b):
        for d in set(a.keys()).union(b.keys()):
            if d in a and d in b:
                if type(a[d]) == type(b[d]):
                    if not isinstance(a[d], dict):
                        ret = list({a[d], b[d]})
                        if len(ret) == 1: ret = ret[0]
                        yield (d, sorted(ret))
                    else:
                        yield (d, dict(merge(a[d], b[d])))
                else:
                    raise TypeError, "Conflicting key:value type assignment"
            elif d in a:
                yield (d, a[d])
            elif d in b:
                yield (d, b[d])
            else:
                raise KeyError

    return reduce(lambda x, y: dict(merge(x, y)), dicts[1:], dicts[0])

print merge_dicts({1:1,2:{1:2}},{1:2,2:{3:1}},{4:4})

आउटपुट: {१: [१, २], २: {१: २, ३: १}, ४: ४}


2

चूंकि तानाशाह सेट ऑपरेशन का समर्थन करते हैं, इसलिए मैं jterrace के उत्तर को बहुत सरल बनाने में सक्षम था।

def merge(dict1, dict2):
    for k in dict1.keys() - dict2.keys():
        yield (k, dict1[k])

    for k in dict2.keys() - dict1.keys():
        yield (k, dict2[k])

    for k in dict1.keys() & dict2.keys():
        yield (k, dict(merge(dict1[k], dict2[k])))

एक गैर-तानाशाह के साथ एक तानाशाही को जोड़ने का कोई भी प्रयास (तकनीकी रूप से, एक 'कुंजियों की विधि के साथ एक वस्तु और एक' कुंजियों की विधि 'के बिना एक वस्तु) एक गुण को बढ़ाएगा। इसमें फ़ंक्शन और पुनरावर्ती कॉल के लिए प्रारंभिक कॉल दोनों शामिल हैं। यह वही है जो मैं चाहता था इसलिए मैंने इसे छोड़ दिया। आप आसानी से रिकर्सिव कॉल द्वारा फेंके गए एट्रीब्यूटर्स को आसानी से पकड़ सकते हैं और फिर किसी भी मूल्य को प्राप्त कर सकते हैं।


2

लघु-एन-मीठा:

from collections.abc import MutableMapping as Map

def nested_update(d, v):
"""
Nested update of dict-like 'd' with dict-like 'v'.
"""

for key in v:
    if key in d and isinstance(d[key], Map) and isinstance(v[key], Map):
        nested_update(d[key], v[key])
    else:
        d[key] = v[key]

यह अजगर की dict.updateविधि की तरह काम करता है (और निर्माण करता है) । यह रिटर्न None(आप हमेशा return dपसंद आने पर जोड़ सकते हैं) क्योंकि यह dइन-प्लेस को अपडेट करता है। में कीज़ vकिसी भी मौजूदा कुंजी को अधिलेखित कर देगी d(यह तानाशाह की सामग्री की व्याख्या करने की कोशिश नहीं करता है)।

यह अन्य ("तानाशाह की तरह") मैपिंग के लिए भी काम करेगा।


1

कोड निश्चित रूप से मर्ज संघर्षों को हल करने के लिए आपके नियमों पर निर्भर करेगा। यहाँ एक ऐसा संस्करण है जो मनमाने ढंग से तर्कों को ले सकता है और किसी भी वस्तु उत्परिवर्तन का उपयोग किए बिना उन्हें एक मनमानी गहराई तक पुन: विलय कर सकता है। मर्ज संघर्षों को हल करने के लिए यह निम्नलिखित नियमों का उपयोग करता है:

  • शब्दकोश गैर-तानाशाही मूल्यों पर पूर्वता लेते हैं ( {"foo": {...}}पूर्वता लेता है {"foo": "bar"})
  • बाद में तर्क पहले तर्क से प्राथमिकता दी जाती है (यदि आप मर्ज {"a": 1}, {"a", 2}और {"a": 3}क्रम में, परिणाम होगा {"a": 3})
try:
    from collections import Mapping
except ImportError:
    Mapping = dict

def merge_dicts(*dicts):                                                            
    """                                                                             
    Return a new dictionary that is the result of merging the arguments together.   
    In case of conflicts, later arguments take precedence over earlier arguments.   
    """                                                                             
    updated = {}                                                                    
    # grab all keys                                                                 
    keys = set()                                                                    
    for d in dicts:                                                                 
        keys = keys.union(set(d))                                                   

    for key in keys:                                                                
        values = [d[key] for d in dicts if key in d]                                
        # which ones are mapping types? (aka dict)                                  
        maps = [value for value in values if isinstance(value, Mapping)]            
        if maps:                                                                    
            # if we have any mapping types, call recursively to merge them          
            updated[key] = merge_dicts(*maps)                                       
        else:                                                                       
            # otherwise, just grab the last value we have, since later arguments    
            # take precedence over earlier arguments                                
            updated[key] = values[-1]                                               
    return updated  

1

मेरे पास दो शब्दकोश ( aऔर b) थे, जिनमें से प्रत्येक में किसी भी संख्या में नेस्टेड शब्दकोश हो सकते थे। मैं bपूर्ववर्ती होने के साथ, उन्हें पुन: विलय करना चाहता था a

पेड़ों के रूप में नेस्टेड शब्दकोशों को ध्यान में रखते हुए, जो मैं चाहता था:

  • अद्यतन करने के लिए aताकि हर पत्ती के लिए हर मार्ग में bप्रतिनिधित्व किया जाएगाa
  • aएक पत्ती में इसी पथ में पाया जाता है , तो उपप्रकारों को अधिलेखित करने के लिएb
    • अपरिवर्तनीय बनाए रखें कि सभी bपत्ती नोड्स लीफ्स रहें।

मौजूदा उत्तर मेरे स्वाद के लिए थोड़े जटिल थे और शेल्फ पर कुछ विवरण छोड़ गए थे। मैंने निम्नलिखित को एक साथ हैक किया, जो मेरे डेटा सेट के लिए यूनिट परीक्षण पास करता है।

  def merge_map(a, b):
    if not isinstance(a, dict) or not isinstance(b, dict):
      return b

    for key in b.keys():
      a[key] = merge_map(a[key], b[key]) if key in a else b[key]
    return a

उदाहरण (स्पष्टता के लिए स्वरूपित):

 a = {
    1 : {'a': 'red', 
         'b': {'blue': 'fish', 'yellow': 'bear' },
         'c': { 'orange': 'dog'},
    },
    2 : {'d': 'green'},
    3: 'e'
  }

  b = {
    1 : {'b': 'white'},
    2 : {'d': 'black'},
    3: 'e'
  }


  >>> merge_map(a, b)
  {1: {'a': 'red', 
       'b': 'white',
       'c': {'orange': 'dog'},},
   2: {'d': 'black'},
   3: 'e'}

जिन रास्तों bको बनाए रखने की आवश्यकता थी, वे थे:

  • 1 -> 'b' -> 'white'
  • 2 -> 'd' -> 'black'
  • 3 -> 'e'

a के अनूठे और गैर-विरोधी रास्ते थे:

  • 1 -> 'a' -> 'red'
  • 1 -> 'c' -> 'orange' -> 'dog'

इसलिए वे अभी भी मर्ज किए गए नक्शे में दर्शाए गए हैं।


1

मेरे पास एक पुनरावृत्त समाधान है - बड़े डिकट्स और उनमें से बहुत से बेहतर काम करता है (उदाहरण के लिए jsons आदि):

import collections


def merge_dict_with_subdicts(dict1: dict, dict2: dict) -> dict:
    """
    similar behaviour to builtin dict.update - but knows how to handle nested dicts
    """
    q = collections.deque([(dict1, dict2)])
    while len(q) > 0:
        d1, d2 = q.pop()
        for k, v in d2.items():
            if k in d1 and isinstance(d1[k], dict) and isinstance(v, dict):
                q.append((d1[k], v))
            else:
                d1[k] = v

    return dict1

ध्यान दें कि यह d2 में मूल्य का उपयोग d1 को ओवरराइड करने के लिए करेगा, यदि वे दोनों डाइक नहीं हैं। (अजगर के समान dict.update())

कुछ परीक्षण:

def test_deep_update():
    d = dict()
    merge_dict_with_subdicts(d, {"a": 4})
    assert d == {"a": 4}

    new_dict = {
        "b": {
            "c": {
                "d": 6
            }
        }
    }
    merge_dict_with_subdicts(d, new_dict)
    assert d == {
        "a": 4,
        "b": {
            "c": {
                "d": 6
            }
        }
    }

    new_dict = {
        "a": 3,
        "b": {
            "f": 7
        }
    }
    merge_dict_with_subdicts(d, new_dict)
    assert d == {
        "a": 3,
        "b": {
            "c": {
                "d": 6
            },
            "f": 7
        }
    }

    # test a case where one of the dicts has dict as value and the other has something else
    new_dict = {
        'a': {
            'b': 4
        }
    }
    merge_dict_with_subdicts(d, new_dict)
    assert d['a']['b'] == 4

मैंने लगभग ~ 1200 डिक्ट्स के साथ परीक्षण किया है - इस पद्धति में 0.4 सेकंड लगे, जबकि पुनरावर्ती समाधान में ~ 2.5 सेकंड लगे।


0

इस से सभी आइटम विलय में मदद करनी चाहिए dict2में dict1:

for item in dict2:
    if item in dict1:
        for leaf in dict2[item]:
            dict1[item][leaf] = dict2[item][leaf]
    else:
        dict1[item] = dict2[item]

कृपया इसका परीक्षण करें और हमें बताएं कि क्या यह आप चाहते हैं।

संपादित करें:

उपर्युक्त समाधान केवल एक स्तर को मिलाता है, लेकिन ओपी द्वारा दिए गए उदाहरण को सही ढंग से हल करता है। कई स्तरों को मर्ज करने के लिए, पुनरावृत्ति का उपयोग किया जाना चाहिए।


1
उन्हें घोंसले की मनमानी गहराई मिली है
agf

यह बस के रूप में फिर से लिखा जा सकता है for k,v in dict2.iteritems(): dict1.setdefault(k,{}).update(v)। लेकिन जैसा कि @agf ने बताया, यह नेस्टेड डाइक को मर्ज नहीं करता है।
शॉन चिन

@agf: सही है, इसलिए ऐसा लगता है कि ओपी को पुनरावृत्ति को हल करने की जरूरत है। इस तथ्य के लिए धन्यवाद कि शब्दकोश सुपाच्य हैं, यह किया जाना काफी आसान होना चाहिए। लेकिन मुझे लगता है सवाल विशिष्ट पर्याप्त बताने के लिए जब हम गहराई के विभिन्न स्तरों के साथ स्थानों के साथ आने से होना चाहिए (उदाहरण के लिए। मर्ज करने की कोशिश कर नहीं है {'a':'b'}के साथ {'a':{'c':'d'})।
ताडेक

0

मैं आपके समाधानों का परीक्षण कर रहा हूं और अपनी परियोजना में इसका उपयोग करने का निर्णय लिया है:

def mergedicts(dict1, dict2, conflict, no_conflict):
    for k in set(dict1.keys()).union(dict2.keys()):
        if k in dict1 and k in dict2:
            yield (k, conflict(dict1[k], dict2[k]))
        elif k in dict1:
            yield (k, no_conflict(dict1[k]))
        else:
            yield (k, no_conflict(dict2[k]))

dict1 = {1:{"a":"A"}, 2:{"b":"B"}}
dict2 = {2:{"c":"C"}, 3:{"d":"D"}}

#this helper function allows for recursion and the use of reduce
def f2(x, y):
    return dict(mergedicts(x, y, f2, lambda x: x))

print dict(mergedicts(dict1, dict2, f2, lambda x: x))
print dict(reduce(f2, [dict1, dict2]))

पैरामीटर के रूप में कार्य करना अन्य सभी पुनरावर्ती समाधानों के रूप में व्यवहार करने के लिए jterrace समाधान का विस्तार करना महत्वपूर्ण है।


0

सबसे आसान तरीका मैं सोच सकता हूं:

#!/usr/bin/python

from copy import deepcopy
def dict_merge(a, b):
    if not isinstance(b, dict):
        return b
    result = deepcopy(a)
    for k, v in b.iteritems():
        if k in result and isinstance(result[k], dict):
                result[k] = dict_merge(result[k], v)
        else:
            result[k] = deepcopy(v)
    return result

a = {1:{"a":'A'}, 2:{"b":'B'}}
b = {2:{"c":'C'}, 3:{"d":'D'}}

print dict_merge(a,b)

आउटपुट:

{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}

0

मैं यहाँ एक और थोड़ा अलग समाधान है:

def deepMerge(d1, d2, inconflict = lambda v1,v2 : v2) :
''' merge d2 into d1. using inconflict function to resolve the leaf conflicts '''
    for k in d2:
        if k in d1 : 
            if isinstance(d1[k], dict) and isinstance(d2[k], dict) :
                deepMerge(d1[k], d2[k], inconflict)
            elif d1[k] != d2[k] :
                d1[k] = inconflict(d1[k], d2[k])
        else :
            d1[k] = d2[k]
    return d1

डिफ़ॉल्ट रूप से यह दूसरे तानाशाह से मूल्यों के पक्ष में टकराव को हल करता है, लेकिन आप इसे आसानी से ओवरराइड कर सकते हैं, कुछ चुड़ैल के साथ आप इसके अपवादों को छोड़ भी सकते हैं। :)।


0
class Utils(object):

    """

    >>> a = { 'first' : { 'all_rows' : { 'pass' : 'dog', 'number' : '1' } } }
    >>> b = { 'first' : { 'all_rows' : { 'fail' : 'cat', 'number' : '5' } } }
    >>> Utils.merge_dict(b, a) == { 'first' : { 'all_rows' : { 'pass' : 'dog', 'fail' : 'cat', 'number' : '5' } } }
    True

    >>> main = {'a': {'b': {'test': 'bug'}, 'c': 'C'}}
    >>> suply = {'a': {'b': 2, 'd': 'D', 'c': {'test': 'bug2'}}}
    >>> Utils.merge_dict(main, suply) == {'a': {'b': {'test': 'bug'}, 'c': 'C', 'd': 'D'}}
    True

    """

    @staticmethod
    def merge_dict(main, suply):
        """
        获取融合的字典,以main为主,suply补充,冲突时以main为准
        :return:
        """
        for key, value in suply.items():
            if key in main:
                if isinstance(main[key], dict):
                    if isinstance(value, dict):
                        Utils.merge_dict(main[key], value)
                    else:
                        pass
                else:
                    pass
            else:
                main[key] = value
        return main

if __name__ == '__main__':
    import doctest
    doctest.testmod()

0

हे, मुझे भी वही समस्या थी, लेकिन मैं एक समाधान के बावजूद और मैं इसे यहां पोस्ट करूंगा, अगर यह दूसरों के लिए भी उपयोगी है, तो मूल रूप से नेस्टेड शब्दकोशों को विलय करना और मूल्यों को जोड़ना भी, मेरे लिए मुझे कुछ संभावनाओं की गणना करने की आवश्यकता थी, इसलिए एक महान काम किया:

#used to copy a nested dict to a nested dict
def deepupdate(target, src):
    for k, v in src.items():
        if k in target:
            for k2, v2 in src[k].items():
                if k2 in target[k]:
                    target[k][k2]+=v2
                else:
                    target[k][k2] = v2
        else:
            target[k] = copy.deepcopy(v)

उपरोक्त विधि का उपयोग करके हम विलय कर सकते हैं:

लक्ष्य = {'6,6': {'6,63': 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 1} , '6,63': {'63, 4 ': 1}}

src = {'5,4': {'4,4': 1}, '5,5': {'5,4': 1}, '4,4': {'4,3': 1} }

और यह बन जाएगा: {'5,5': {'5,4': 1}, '5,4': {'4,4': 1}, '6,6': {'6,63' : 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 2},' 6,63 ': {'63, 4': 1 }}

यहां भी बदलाव देखें

लक्ष्य = {'6,6': {'6,63': 1}, '6,63': {'63, 4 ': 1}, ' 4,4 ': {' 4,3 ': 1} , '63, 4 ': {' 4,4 ': 1}}

src = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '4,4': {'4,9': 1} , '3,4': {'4,4': 1}, '5,5': {'5,4': 1}}

मर्ज = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '6,63': {'63, 4 ': 1} , '5,5': {'5,4': 1}, '6,6': {'6,63': 1}, '3,4': {'4,4': 1}, ' 63,4 ': {' 4,4 ': 1}, ' 4,4 ': {' 4,3 ': 1,' 4,9 ': 1} }

प्रतिलिपि के लिए आयात जोड़ना भी न भूलें:

import copy

0
from collections import defaultdict
from itertools import chain

class DictHelper:

@staticmethod
def merge_dictionaries(*dictionaries, override=True):
    merged_dict = defaultdict(set)
    all_unique_keys = set(chain(*[list(dictionary.keys()) for dictionary in dictionaries]))  # Build a set using all dict keys
    for key in all_unique_keys:
        keys_value_type = list(set(filter(lambda obj_type: obj_type != type(None), [type(dictionary.get(key, None)) for dictionary in dictionaries])))
        # Establish the object type for each key, return None if key is not present in dict and remove None from final result
        if len(keys_value_type) != 1:
            raise Exception("Different objects type for same key: {keys_value_type}".format(keys_value_type=keys_value_type))

        if keys_value_type[0] == list:
            values = list(chain(*[dictionary.get(key, []) for dictionary in dictionaries]))  # Extract the value for each key
            merged_dict[key].update(values)

        elif keys_value_type[0] == dict:
            # Extract all dictionaries by key and enter in recursion
            dicts_to_merge = list(filter(lambda obj: obj != None, [dictionary.get(key, None) for dictionary in dictionaries]))
            merged_dict[key] = DictHelper.merge_dictionaries(*dicts_to_merge)

        else:
            # if override => get value from last dictionary else make a list of all values
            values = list(filter(lambda obj: obj != None, [dictionary.get(key, None) for dictionary in dictionaries]))
            merged_dict[key] = values[-1] if override else values

    return dict(merged_dict)



if __name__ == '__main__':
  d1 = {'aaaaaaaaa': ['to short', 'to long'], 'bbbbb': ['to short', 'to long'], "cccccc": ["the is a test"]}
  d2 = {'aaaaaaaaa': ['field is not a bool'], 'bbbbb': ['field is not a bool']}
  d3 = {'aaaaaaaaa': ['filed is not a string', "to short"], 'bbbbb': ['field is not an integer']}
  print(DictHelper.merge_dictionaries(d1, d2, d3))

  d4 = {"a": {"x": 1, "y": 2, "z": 3, "d": {"x1": 10}}}
  d5 = {"a": {"x": 10, "y": 20, "d": {"x2": 20}}}
  print(DictHelper.merge_dictionaries(d4, d5))

आउटपुट:

{'bbbbb': {'to long', 'field is not an integer', 'to short', 'field is not a bool'}, 
'aaaaaaaaa': {'to long', 'to short', 'filed is not a string', 'field is not a bool'}, 
'cccccc': {'the is a test'}}

{'a': {'y': 20, 'd': {'x1': 10, 'x2': 20}, 'z': 3, 'x': 10}}

हालांकि यह कोड इस प्रश्न का उत्तर दे सकता है, कि यह कोड क्यों और / या इस बारे में अतिरिक्त संदर्भ प्रदान करता है कि यह प्रश्न इसके दीर्घकालिक मूल्य में सुधार करता है।
xiawi

मुझे लगता है कि यह एक या एक से अधिक नेस्टेड डिक्शनरी को मर्ज करने का एक सामान्य कार्यान्वयन है, जिसमें विचार किया जाएगा कि किस प्रकार की वस्तुओं को मार दिया जाएगा
Dorcioman

0

पैकेज पर एक नज़र डालेंtoolz

import toolz
dict1={1:{"a":"A"},2:{"b":"B"}}
dict2={2:{"c":"C"},3:{"d":"D"}}
toolz.merge_with(toolz.merge,dict1,dict2)

देता है

{1: {'a': 'A'}, 2: {'b': 'B', 'c': 'C'}, 3: {'d': 'D'}}


0

और बस एक और मामूली बदलाव:

यहाँ एक शुद्ध python3 सेट आधारित गहन अद्यतन फ़ंक्शन है। यह एक समय में एक स्तर के माध्यम से लूप द्वारा नेस्टेड शब्दकोशों को अपडेट करता है और शब्दकोश मूल्यों के प्रत्येक अगले स्तर को अपडेट करने के लिए खुद को कॉल करता है:

def deep_update(dict_original, dict_update):
    if isinstance(dict_original, dict) and isinstance(dict_update, dict):
        output=dict(dict_original)
        keys_original=set(dict_original.keys())
        keys_update=set(dict_update.keys())
        similar_keys=keys_original.intersection(keys_update)
        similar_dict={key:deep_update(dict_original[key], dict_update[key]) for key in similar_keys}
        new_keys=keys_update.difference(keys_original)
        new_dict={key:dict_update[key] for key in new_keys}
        output.update(similar_dict)
        output.update(new_dict)
        return output
    else:
        return dict_update

एक सरल उदाहरण:

x={'a':{'b':{'c':1, 'd':1}}}
y={'a':{'b':{'d':2, 'e':2}}, 'f':2}

print(deep_update(x, y))
>>> {'a': {'b': {'c': 1, 'd': 2, 'e': 2}}, 'f': 2}

0

कैसे एक और जवाब के बारे में? यह भी उत्परिवर्तन / दुष्प्रभाव से बचा जाता है:

def merge(dict1, dict2):
    output = {}

    # adds keys from `dict1` if they do not exist in `dict2` and vice-versa
    intersection = {**dict2, **dict1}

    for k_intersect, v_intersect in intersection.items():
        if k_intersect not in dict1:
            v_dict2 = dict2[k_intersect]
            output[k_intersect] = v_dict2

        elif k_intersect not in dict2:
            output[k_intersect] = v_intersect

        elif isinstance(v_intersect, dict):
            v_dict2 = dict2[k_intersect]
            output[k_intersect] = merge(v_intersect, v_dict2)

        else:
            output[k_intersect] = v_intersect

    return output
dict1 = {1:{"a":{"A"}}, 2:{"b":{"B"}}}
dict2 = {2:{"c":{"C"}}, 3:{"d":{"D"}}}
dict3 = {1:{"a":{"A"}}, 2:{"b":{"B"},"c":{"C"}}, 3:{"d":{"D"}}}

assert dict3 == merge(dict1, dict2)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.