पायथन में एक ही अभिव्यक्ति में दो शब्दकोशों को कैसे मिलाऊं?


4780

मेरे पास दो पायथन शब्दकोश हैं, और मैं एक एकल अभिव्यक्ति लिखना चाहता हूं जो इन दो शब्दकोशों को वापस लौटाता है, विलीन हो जाता है। यह update()तरीका वही होगा जो मुझे चाहिए, अगर यह एक शब्दकोश में जगह को संशोधित करने के बजाय अपना परिणाम लौटाता है।

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

मैं उस अंतिम मर्ज किए गए शब्दकोश को कैसे प्राप्त कर सकता हूं z, नहीं x?

(अतिरिक्त स्पष्ट होने के लिए, पिछले-एक-जीत संघर्ष-हैंडलिंग वह dict.update()है जो मैं भी देख रहा हूं।)


इस मौके पर कि आप Python 3.9 अल्फा का उपयोग कर रहे हैं, बस उपयोग करेंz = x | y
The Daleks

जवाबों:


5685

मैं एक ही अभिव्यक्ति में दो पायथन शब्दकोशों का विलय कैसे कर सकता हूं?

शब्दकोशों के लिए xऔर y, उन लोगों की जगह zसे मूल्यों के साथ एक उथले विलय शब्दकोष बन जाता है ।yx

  • पायथन 3.5 या अधिक में:

    z = {**x, **y}
  • पायथन 2 में, (या 3.4 या निम्न) एक फ़ंक्शन लिखें:

    def merge_two_dicts(x, y):
        z = x.copy()   # start with x's keys and values
        z.update(y)    # modifies z with y's keys and values & returns None
        return z
    

    और अब:

    z = merge_two_dicts(x, y)
  • पायथन में 3.9.0a4 या उससे अधिक (अंतिम रिलीज की तारीख लगभग अक्टूबर 2020): PEP-584 , यहां चर्चा की गई , इसे और सरल बनाने के लिए लागू किया गया:

    z = x | y          # NOTE: 3.9+ ONLY

व्याख्या

मान लें कि आपके पास दो डाइक हैं और आप मूल डाइक को बदले बिना उन्हें एक नए अधिमान में विलय करना चाहते हैं:

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

वांछित परिणाम zविलय के साथ एक नया शब्दकोश ( ) प्राप्त करने के लिए है , और पहले से उन पर ओवरराइटिंग करने वाले दूसरे तानाशाह के मूल्य हैं।

>>> z
{'a': 1, 'b': 3, 'c': 4}

इसके लिए एक नया वाक्यविन्यास, पीईपी 448 में प्रस्तावित और पायथन 3.5 के रूप में उपलब्ध है

z = {**x, **y}

और यह वास्तव में एक ही अभिव्यक्ति है।

ध्यान दें कि हम शाब्दिक अंकन के साथ भी विलय कर सकते हैं:

z = {**x, 'foo': 1, 'bar': 2, **y}

और अब:

>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}

अब इसे 3.5, PEP 478 के रिलीज़ शेड्यूल में लागू किया जा रहा है , और इसने अब Python 3.5 दस्तावेज़ में व्हाट्स न्यू में अपना रास्ता बना लिया है ।

हालाँकि, कई संगठन अभी भी पायथन 2 पर हैं, आप इसे पीछे की ओर संगत तरीके से करने की इच्छा कर सकते हैं। पायथन 2 और पायथन 3.0-3.4 में उपलब्ध क्लासिकल पाइथोनिक तरीका, इसे दो-चरणीय प्रक्रिया के रूप में करना है:

z = x.copy()
z.update(y) # which returns None since it mutates z

दोनों दृष्टिकोणों में, yदूसरे स्थान पर आएगा और इसके मूल्य हमारे मूल्यों को प्रतिस्थापित करेंगे x, इस प्रकार हमारे अंतिम परिणाम में 'b'इंगित करेंगे 3

पायथन 3.5 पर अभी तक नहीं, लेकिन एक एकल अभिव्यक्ति चाहते हैं

यदि आप अभी तक पायथन 3.5 पर नहीं हैं, या पिछड़े-संगत कोड लिखने की आवश्यकता है, और आप इसे एकल अभिव्यक्ति में चाहते हैं , तो सबसे अच्छा प्रदर्शन सही दृष्टिकोण है, जबकि इसे एक फ़ंक्शन में रखना है:

def merge_two_dicts(x, y):
    """Given two dicts, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z

और तब आपके पास एक ही अभिव्यक्ति है:

z = merge_two_dicts(x, y)

आप शून्य से बहुत बड़ी संख्या में, अपरिभाषित संख्याओं को विलय करने के लिए एक समारोह बना सकते हैं:

def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

यह समारोह सभी डाइट के लिए पायथन 2 और 3 में काम करेगा। दिए गए dicts जैसे aकरने के लिए g:

z = merge_dicts(a, b, c, d, e, f, g) 

और प्रमुख मूल्य जोड़े , gडीकट्स aसे पहले f, और इसी तरह से पूर्वता लेंगे ।

अन्य उत्तरों की आलोचना

पूर्व स्वीकृत उत्तर में आप जो देखते हैं उसका उपयोग न करें:

z = dict(x.items() + y.items())

पायथन 2 में, आप प्रत्येक श्रुत के लिए स्मृति में दो सूचियाँ बनाते हैं, स्मृति में तीसरी सूची बनाते हैं, जिसमें पहले दो पुट की लंबाई के बराबर लंबाई होती है, और फिर सभी तीन सूचियों को रचने के लिए छोड़ देते हैं। पायथन 3 में, यह विफल हो जाएगा क्योंकि आप दो dict_itemsवस्तुओं को एक साथ जोड़ रहे हैं, दो सूचियों में नहीं -

>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

और आपको स्पष्ट रूप से उन्हें सूचियों के रूप में बनाना होगा, जैसे z = dict(list(x.items()) + list(y.items()))। यह संसाधनों और कम्प्यूटेशन पावर की बर्बादी है।

इसी तरह, items()पायथन 3 ( viewitems()पायथन 2.7 में) का संघ लेना तब भी विफल हो जाएगा जब मान अप्रभावी वस्तुएं हैं (जैसे सूचियां, उदाहरण के लिए)। यहां तक ​​कि अगर आपके मूल्यों को धोने योग्य है, क्योंकि सेट शब्दार्थ रूप से अनियंत्रित हैं, व्यवहार पूर्वता के संबंध में अपरिभाषित है। तो यह मत करो:

>>> c = dict(a.items() | b.items())

यह उदाहरण प्रदर्शित करता है कि मूल्यों के अस्वाभाविक होने पर क्या होता है:

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

यहां एक उदाहरण दिया गया है जहां y की पूर्ववर्तीता होनी चाहिए, लेकिन इसके बजाय x का मान सेटों के मनमाने आदेश के कारण बरकरार रखा गया है:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

एक और हैक जिसका आपको उपयोग नहीं करना चाहिए:

z = dict(x, **y)

यह dictकंस्ट्रक्टर का उपयोग करता है , और बहुत तेज़ और मेमोरी कुशल है (यहां तक ​​कि हमारी दो-चरणीय प्रक्रिया की तुलना में थोड़ा अधिक) लेकिन जब तक आप ठीक से नहीं जानते कि यहां क्या हो रहा है (यानी, दूसरे डिक्टेट को तानाशाही के लिए तर्क के रूप में पारित किया जा रहा है) कंस्ट्रक्टर), यह पढ़ना मुश्किल है, यह इच्छित उपयोग नहीं है, और इसलिए यह पायथोनिक नहीं है।

यहाँ django में उपयोग किए जा रहे उपयोग का एक उदाहरण है ।

डायट्स को हेज़ल कीज़ (जैसे फ्रोज़ेनसेट्स या ट्यूपल्स) लेने का इरादा है, लेकिन पाइथन 3 में यह विधि विफल हो जाती है जब चाबियाँ स्ट्रिंग्स नहीं होती हैं।

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

से मेलिंग सूची , गुइडो van Rossum, भाषा के निर्माता ने लिखा है:

मैं तानाशाह ({}, ** {1: 3}) को अवैध घोषित करने के साथ ठीक हूं, क्योंकि आखिरकार यह ** तंत्र का दुरुपयोग है।

तथा

जाहिर तौर पर तानाशाही (x, ** y) "कॉल x.update (y) और रिटर्न x" के लिए "कूल हैक" के रूप में घूम रहा है। व्यक्तिगत रूप से मैं इसे शांत से अधिक तुच्छ समझता हूं।

यह मेरी समझ है (साथ ही भाषा के निर्माता की समझ ) कि dict(**y)पठनीय उद्देश्यों के लिए डिकेट बनाने के लिए इसका उपयोग करना है, जैसे:

dict(a=1, b=10, c=11)

के बजाय

{'a': 1, 'b': 10, 'c': 11}

टिप्पणियों का जवाब

गुइडो जो कहते हैं, उसके बावजूद, dict(x, **y)यह विशिष्ट विनिर्देश के अनुरूप है, जो कि btw। पायथन 2 और 3 दोनों के लिए काम करता है। यह तथ्य कि यह केवल स्ट्रिंग कीज़ के लिए काम करता है, इसका एक प्रत्यक्ष परिणाम है कि कीवर्ड पैरामीटर कैसे काम करते हैं और तानाशाही की कमी नहीं है। न ही ** ऑपरेटर का उपयोग इस जगह में तंत्र का दुरुपयोग है, वास्तव में ** को कीवर्ड के रूप में dicts पास करने के लिए डिज़ाइन किया गया था।

फिर, यह 3 के लिए काम नहीं करता है जब चाबियाँ गैर-तार होती हैं। अंतर्निहित कॉलिंग कॉन्ट्रैक्ट यह है कि नेमस्पेस साधारण डाइक लेते हैं, जबकि उपयोगकर्ताओं को केवल कीवर्ड तर्क पास करने चाहिए जो स्ट्रिंग्स हैं। अन्य सभी कॉलबलों ने इसे लागू किया। dictपायथन 2 में इस स्थिरता को तोड़ दिया:

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

इस असंगति को पायथन (Pypy, Jython, IronPython) के अन्य कार्यान्वयनों को देखते हुए बुरा माना गया। इस प्रकार यह पायथन 3 में तय किया गया था, क्योंकि यह उपयोग एक ब्रेकिंग परिवर्तन हो सकता है।

मैं आपके सामने प्रस्तुत करता हूं कि यह जानबूझकर कोड लिखने के लिए दुर्भावनापूर्ण अक्षमता है जो केवल एक भाषा के एक संस्करण में काम करता है या केवल कुछ मनमाने बाधाओं को देखते हुए काम करता है।

अधिक टिप्पणियाँ:

dict(x.items() + y.items()) अभी भी पायथन 2 के लिए सबसे पठनीय समाधान है। पठनीयता मायने रखती है।

मेरी प्रतिक्रिया: merge_two_dicts(x, y)वास्तव में मुझे बहुत स्पष्ट लगता है, अगर हम वास्तव में पठनीयता के बारे में चिंतित हैं। और यह आगे संगत नहीं है, क्योंकि पायथन 2 तेजी से पदावनत है।

{**x, **y}नेस्टेड शब्दकोशों को संभालने के लिए प्रतीत नहीं होता है। नेस्टेड कुंजियों की सामग्री को केवल ओवरराइट किया जाता है, विलय नहीं किया जाता है [...] मैं समाप्त हो गया इन उत्तरों द्वारा जलाया जा रहा है जो पुनरावृत्ति में विलय नहीं करते हैं और मुझे आश्चर्य हुआ कि किसी ने भी इसका उल्लेख नहीं किया। शब्द "मर्जिंग" की मेरी व्याख्या में इन उत्तरों का वर्णन है "एक दूसरे के साथ तानाशाही को अद्यतन करना", और विलय नहीं।

हाँ। मुझे आपको उस प्रश्न का वापस उल्लेख करना चाहिए, जो दो शब्दकोशों के उथले विलय के लिए पूछ रहा है , पहले मूल्यों को दूसरे के द्वारा एक ही अभिव्यक्ति में अधिलेखित किया जा रहा है।

शब्दकोशों के दो शब्दकोश मानते हुए, कोई भी उन्हें एक ही फ़ंक्शन में पुन: विलय कर सकता है, लेकिन आपको सावधान रहना चाहिए कि किसी भी स्रोत से डाइक को संशोधित न करें, और इससे बचने का सबसे सुरक्षित तरीका है कि मूल्यों को असाइन करते समय एक प्रतिलिपि बनाएं। चूंकि चाबियां हवादार होनी चाहिए और आमतौर पर अपरिवर्तनीय होती हैं, इसलिए उन्हें कॉपी करना व्यर्थ है:

from copy import deepcopy

def dict_of_dicts_merge(x, y):
    z = {}
    overlapping_keys = x.keys() & y.keys()
    for key in overlapping_keys:
        z[key] = dict_of_dicts_merge(x[key], y[key])
    for key in x.keys() - overlapping_keys:
        z[key] = deepcopy(x[key])
    for key in y.keys() - overlapping_keys:
        z[key] = deepcopy(y[key])
    return z

उपयोग:

>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}

अन्य मूल्य प्रकारों के लिए आकस्मिकताओं के साथ आना इस प्रश्न के दायरे से परे है, इसलिए मैं आपको "शब्दकोशों के विलय के शब्दकोश" पर कैनोनिकल प्रश्न के उत्तर में बताऊंगा

कम कलाकार लेकिन सही विज्ञापन-होक्स

ये दृष्टिकोण कम प्रदर्शन वाले हैं, लेकिन वे सही व्यवहार प्रदान करेंगे। वे हो जाएगा बहुत कम की तुलना में performant copyऔर updateया नए unpacking क्योंकि वे अमूर्त की एक उच्च स्तर पर प्रत्येक कुंजी-मान पेयर के माध्यम से पुनरावृति है, लेकिन वे करते हैं (उत्तरार्द्ध dicts पूर्वता है) प्राथमिकता का क्रम का सम्मान

तुम भी एक तानाशाही समझ के अंदर मैन्युअल रूप से dicts श्रृंखला कर सकते हैं:

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

या अजगर 2.6 में (और संभवतया 2.4 के रूप में जल्दी जब जनरेटर अभिव्यक्ति शुरू की गई थी):

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain सही क्रम में कुंजी-मूल्य जोड़े पर चलने वालों की श्रृंखला करेगा:

import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))

क्षमता का परिक्षण

मैं केवल सही ढंग से व्यवहार करने के लिए ज्ञात usages के प्रदर्शन विश्लेषण करने जा रहा हूँ।

import timeit

निम्नलिखित Ubuntu 14.04 पर किया जाता है

पायथन 2.7 में (सिस्टम पायथन):

>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934

पायथन 3.5 में (पीपीए के बारे में जानकारी):

>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287

शब्दकोश पर संसाधन


9
@ मोहम्मदअज़िम "केवल तार" केवल कॉलबैक में कीवर्ड तर्क विस्तार पर लागू होता है, सामान्यीकृत अनपैकिंग सिंटैक्स पर नहीं। प्रदर्शित करने के लिए है कि इस काम करता है: {**{(0, 1):2}}->{(0, 1): 2}
हारून हॉल

36
जैसे छोटे जवाब z = {**x, **y}वास्तव में मुझे उत्तेजित करते हैं
pcko1

1
PEP-0584 स्वीकार किए जाने पर इसे बदला जा सकता है। एक नया यूनियन ऑपरेटर निम्नलिखित सिंटैक्स के साथ लागू किया जाएगा:x | y
कालम डेलानी

2
जब किसी उत्तर को शीर्ष पर सारांश की आवश्यकता होती है तो वह बहुत लंबा होता है।
ग्रिंगो सुवे

2
हाय, शीर्ष एक सारांश है, हाँ। आप पर निर्भर करता है। पूरी बात एक महान ब्लॉग पोस्ट होगी। नोट Py 3.4 और नीचे EOL, 3.5 2020 में EOL के करीब पहुंच रहे हैं।
ग्रिंगो सुवे

1615

आपके मामले में, आप क्या कर सकते हैं:

z = dict(x.items() + y.items())

यह, जैसा कि आप चाहते हैं, अंतिम तानाशाही में डाल दिया है z, और कुंजी के लिए मूल्य bठीक से दूसरे द्वारा ओवरराइड किया जाना चाहिए ( y) तानाशाह का मूल्य:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

यदि आप पायथन 3 का उपयोग करते हैं, तो यह केवल थोड़ा अधिक जटिल है। बनाने के लिए z:

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

यदि आप Python संस्करण 3.9.0a4 या अधिक का उपयोग करते हैं, तो आप सीधे उपयोग कर सकते हैं:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = x | y
print(z)

Output: {'a': 1, 'c': 11, 'b': 10}

1
इसका उपयोग न करें क्योंकि यह बहुत अक्षम है। (नीचे दिए गए समय परिणाम देखें।) यह Py2 दिनों में आवश्यक हो सकता है यदि एक आवरण समारोह एक विकल्प नहीं था, लेकिन वे दिन अब अतीत हैं।
ग्रिंगो सुवे

632

एक विकल्प:

z = x.copy()
z.update(y)

83
यह स्पष्ट करने के लिए कि यह प्रश्न द्वारा प्रदान किए गए क्रेटेरा से क्यों नहीं मिलता है: यह एक एकल अभिव्यक्ति नहीं है और यह जेड वापस नहीं करता है।
एलेक्स

2
@neuronet हर ऑन्लाइनर आमतौर पर बस एक ऐसा कोड होता है जो एक अलग घटक में होता है और इसे वहां हल करता है। यह निश्चित रूप से मामलों में से एक है। लेकिन अन्य भाषाओं में इसके लिए अजगर की तुलना में अच्छे निर्माण हैं। और एक संदर्भित पारदर्शी संस्करण है कि यह तत्व है रिटर्न एक अच्छी बात है।
एलेक्स

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

ठीक है, अगर लोग इसे एक oneliner बनाने पर जोर देते हैं, तो आप हमेशा कर सकते हैं (lambda z: z.update(y) or z)(x.copy()): P
Towr

339

एक और, अधिक संक्षिप्त, विकल्प:

z = dict(x, **y)

नोट : यह एक लोकप्रिय उत्तर बन गया है, लेकिन यह इंगित करना महत्वपूर्ण है कि यदि yकोई गैर-स्ट्रिंग कुंजी है, तो यह तथ्य कि यह सब काम करता है एक CPython कार्यान्वयन विवरण का दुरुपयोग है, और यह पायथन 3 में काम नहीं करता है, या PyPy, IronPython, या Jython में। इसके अलावा, गुइडो एक प्रशंसक नहीं है । इसलिए मैं इस तकनीक को फॉरवर्ड-कम्पेटिबल या क्रॉस-इंप्लीमेंटेशन पोर्टेबल कोड के लिए अनुशंसित नहीं कर सकता, जिसका वास्तव में मतलब है कि इसे पूरी तरह से टाला जाना चाहिए।


Python 3 और PyPy और PyPy 3 में ठीक काम करता है , Jython या आयरन से बात नहीं कर सकता। यह देखते हुए कि इस पैटर्न को स्पष्ट रूप से प्रलेखित किया गया है (इस दस्तावेज़ में तीसरा निर्माणकर्ता प्रपत्र देखें) मैं तर्क देता हूं कि यह "कार्यान्वयन विवरण" नहीं है, लेकिन जानबूझकर सुविधा का उपयोग है।
amcgregor

5
@amcgregor आपने कुंजी वाक्यांश को याद किया "यदि y के पास कोई गैर-स्ट्रिंग कुंजी है।" यही Python3 में काम नहीं करता है; तथ्य यह है कि यह सीपी 2 में काम करता है एक कार्यान्वयन विवरण है जिस पर भरोसा नहीं किया जा सकता है। IFF आपकी सभी कुंजियों को स्ट्रिंग्स होने की गारंटी है, यह पूरी तरह से समर्थित विकल्प है।
कार्ल मेयर

213

यह शायद एक लोकप्रिय जवाब नहीं होगा, लेकिन आप लगभग निश्चित रूप से ऐसा नहीं करना चाहते हैं। यदि आप एक प्रति चाहते हैं जो एक मर्ज है, तो प्रतिलिपि का उपयोग करें (या डीकोपी , जो आप चाहते हैं उसके आधार पर) और फिर अपडेट करें। कोड की दो पंक्तियाँ बहुत अधिक पठनीय हैं - अधिक पायथोनिक - .items () + .items () के साथ एकल पंक्ति निर्माण की तुलना में। निहितार्थ की तुलना में स्पष्ट है।

इसके अलावा, जब आप .items () (प्री पायथन 3.0) का उपयोग करते हैं, तो आप एक नई सूची बना रहे होते हैं जिसमें तानाशाह से आइटम होते हैं। यदि आपके शब्दकोश बड़े हैं, तो वह बहुत अधिक ओवरहेड है (मर्ज किए गए तानाशाह के पैदा होते ही दो बड़ी सूचियों को फेंक दिया जाएगा)। अद्यतन () अधिक कुशलता से काम कर सकता है, क्योंकि यह दूसरे तानाशाह आइटम-दर-आइटम के माध्यम से चल सकता है।

समय के संदर्भ में :

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

IMO पहले दो के बीच छोटे मंदी पठनीयता के लिए इसके लायक है। इसके अलावा, शब्दकोश निर्माण के लिए कीवर्ड तर्क केवल पायथन 2.3 में जोड़े गए थे, जबकि कॉपी () और अपडेट () पुराने संस्करणों में काम करेंगे।


149

एक अनुवर्ती उत्तर में, आपने इन दो विकल्पों के सापेक्ष प्रदर्शन के बारे में पूछा:

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

मेरी मशीन पर, कम से कम (काफी सामान्य x86_64 पायथन 2.5.2 चल रहा है), विकल्प z2न केवल छोटा और सरल है, बल्कि काफी तेज भी है। आप timeitपायथन के साथ आने वाले मॉड्यूल का उपयोग करके इसे अपने लिए सत्यापित कर सकते हैं ।

उदाहरण 1: समान शब्दकोष खुद को लगातार 20 पूर्णांक मैप कर रहे हैं:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z23.5 या तो के कारक से जीतता है। अलग-अलग शब्दकोश काफी अलग परिणाम देते हैं, लेकिन z2हमेशा आगे निकलते हैं। (यदि आप एक ही परीक्षण के लिए असंगत परिणाम प्राप्त करते हैं, -rतो डिफ़ॉल्ट 3 से बड़ी संख्या के साथ पास होने का प्रयास करें ।)

उदाहरण 2: पूर्णांक और इसके विपरीत 252 छोटे तार की गैर-अतिव्यापी शब्दकोशों की मैपिंग:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 10. के कारक के बारे में जीतता है। यह मेरी पुस्तक में बहुत बड़ी जीत है!

उन दोनों की तुलना करने के बाद, मुझे आश्चर्य हुआ कि क्या z1खराब प्रदर्शन को दो आइटम सूचियों के निर्माण के ओवरहेड के लिए जिम्मेदार ठहराया जा सकता है, जिसके कारण मुझे आश्चर्य होता है कि क्या यह विविधता बेहतर काम कर सकती है:

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

कुछ त्वरित परीक्षण, उदाहरण के लिए

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

मुझे यह निष्कर्ष निकालने के लिए नेतृत्व करें कि z3कुछ हद तक तेज है z1, लेकिन लगभग उतना तेज नहीं है z2। निश्चित रूप से सभी अतिरिक्त टाइपिंग के लायक नहीं है।

यह चर्चा अभी भी कुछ महत्वपूर्ण याद कर रही है, जो कि दो सूचियों को विलय करने के "स्पष्ट" तरीके के साथ इन विकल्पों के प्रदर्शन की तुलना है: updateविधि का उपयोग करना । भावों के साथ चीजों को एक समान पायदान पर रखने की कोशिश करने के लिए, जिनमें से कोई भी x या y को संशोधित नहीं करता है, मैं इसे इन-प्लेस संशोधित करने के बजाय x की एक प्रतिलिपि बनाने जा रहा हूं, निम्नानुसार:

z0 = dict(x)
z0.update(y)

एक विशिष्ट परिणाम:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

दूसरे शब्दों में, z0और z2लगता है कि अनिवार्य रूप से समान प्रदर्शन है। क्या आपको लगता है कि यह एक संयोग हो सकता है? मैं नही....

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

आप इसे भी लिख सकते हैं

z0 = x.copy()
z0.update(y)

जैसा कि टोनी करता है, लेकिन (आश्चर्यजनक रूप से नहीं) नोटेशन के अंतर से पता चलता है कि प्रदर्शन पर कोई औसत दर्जे का प्रभाव नहीं है। जो भी आपको सही लगे, प्रयोग करें। बेशक, वह यह कहना बिल्कुल सही है कि दो-बयान संस्करण को समझना बहुत आसान है।


5
यह पायथन 3 में काम नहीं करता है; items()प्रवीण नहीं है, और iteritemsमौजूद नहीं है।
अंती हापाला

126

पायथन 3.0 और बाद में , आप collections.ChainMapएक एकल, अद्यतन करने योग्य बनाने के लिए कई समूहों या एक साथ कई मैपिंग का उपयोग कर सकते हैं :

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(ChainMap({}, y, x))
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

पायथन 3.5 और बाद के लिए अपडेट करें : आप PEP 448 विस्तारित शब्दकोश पैकिंग और अनपैकिंग का उपयोग कर सकते हैं । यह तेज़ और आसान है:

>>> x = {'a':1, 'b': 2}
>>> y = y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}

3
लेकिन किसी को चैनपाइप का उपयोग करते समय सतर्क रहना चाहिए कि यदि आपके पास डुप्लिकेट कुंजियाँ हैं, तो पहली मैपिंग से मानों का उपयोग किया जाता है और जब आप कहते हैं delकि एक चैनपाइप सी उस कुंजी की पहली मैपिंग को हटा देगा।
स्लेयर

7
@Prerit आप इससे और क्या करने की उम्मीद करेंगे? यह सामान्य तरीका है जंजीर नामस्थान काम करते हैं। विचार करें कि $ PATH कैसे काम करता है। रास्ते में एक निष्पादन योग्य को हटाने से एक और निष्पादन योग्य नाम आगे अपस्ट्रीम के साथ समाप्त नहीं होता है।
रेमंड हेटिंगर

2
@ रेमंड हेटिंगर मैं सहमत हूँ, बस एक सावधानी जोड़ी। ज्यादातर लोगों को इसके बारे में पता नहीं हो सकता है। : डी
स्लेयर

@Prerit आप इससे dictबचने के लिए कास्ट कर सकते हैं , अर्थात:dict(ChainMap({}, y, x))
wjandrea

112

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

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result

87

पुनरावर्ती / गहरा अद्यतन एक तानाशाही

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

प्रदर्शन:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

आउटपुट:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

संपादन के लिए धन्यवाद rednaw।


1
इस सवाल का जवाब नहीं है। प्रश्न स्पष्ट रूप से एक नया शब्दकोष, z, मूल शब्दकोशों, x और y से पूछता है, y के मानों से x के उन मानों को प्रतिस्थापित कर रहा है - अद्यतन शब्दकोश नहीं। यह उत्तर x से मान जोड़कर y-in-place को संशोधित करता है। इससे भी बदतर, यह इन मूल्यों की नकल नहीं करता है, इसलिए एक और संशोधित शब्दकोश, वाई को संशोधित कर सकता है, और संशोधनों को शब्दकोश एक्स में परिलक्षित किया जा सकता है। @ Jérôme मुझे उम्मीद है कि यह कोड आपके आवेदन के लिए कोई बग पैदा नहीं कर रहा है - कम से कम मूल्यों को कॉपी करने के लिए डीपकोपी का उपयोग करने पर विचार करें।
आरोन हॉल

1
@AaronHall इस बात से सहमत है कि इस सवाल का जवाब नहीं है। लेकिन यह मेरी जरूरत का जवाब देता है। मैं उन सीमाओं को समझता हूं, लेकिन मेरे मामले में यह कोई मुद्दा नहीं है। यह सोचकर, शायद नाम भ्रामक है, क्योंकि यह एक गहरी अवधारणा को जन्म दे सकता है, जो यह प्रदान नहीं करता है। लेकिन यह गहरे घोंसले के शिकार को संबोधित करता है। यहाँ Martellibot से एक और कार्यान्वयन है: stackoverflow.com/questions/3232943/…
Jérôme

71

प्रतिलिपि का उपयोग नहीं करते समय मैं सबसे अच्छा संस्करण सोच सकता था:

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

यह तेजी से कम से कम CPython पर dict(x.items() + y.items())जितना तेज़ है n = copy(a); n.update(b), उतना तेज़ नहीं है । यह संस्करण पायथन 3 में भी काम करता है यदि आप बदल iteritems()जाते हैं items(), जो 2to3 टूल द्वारा स्वचालित रूप से किया जाता है।

व्यक्तिगत रूप से मुझे यह संस्करण सबसे अच्छा लगता है क्योंकि यह काफी अच्छा वर्णन करता है जो मैं एक कार्यात्मक वाक्यविन्यास में चाहता हूं। केवल मामूली समस्या यह है कि यह पूरी तरह से स्पष्ट नहीं करता है कि y से मान एक्स से मानों पर पूर्वता लेता है, लेकिन मुझे विश्वास नहीं है कि यह पता लगाना मुश्किल है।


70

पायथन 3.5 (पीईपी 448) एक अच्छे सिंटैक्स विकल्प की अनुमति देता है:

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

या और भी

final = {'a': 1, 'b': 1, **x, **y}

Python 3.9 में आप भी इस्तेमाल करें | और | = पीईपी 584 से नीचे के उदाहरण के साथ

d = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e
# {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

किस तरह से समाधान से बेहतर है dict(x, **y)? जैसा कि आपने (@CarlMeyer) ने अपने स्वयं के जवाब के नोट में उल्लेख किया है ( stackoverflow.com/a/39858/2798610 ) गुइडो उस समाधान को अवैध मानते हैं ।
ब्लैकअलेज 52

14
Guido dict(x, **y)को (बहुत अच्छा) इस कारण से नापसंद है कि यह yकेवल उन कुंजियों पर निर्भर करता है जो मान्य कीवर्ड तर्क नाम हैं (जब तक कि आप CPython 2.7 का उपयोग कर रहे हैं, जहां तानाशाह कंस्ट्रक्टर धोखा देता है)। यह आक्षेप / प्रतिबंध पीईपी 448 पर लागू नहीं होता है, जो कि **अनधिकृत वाक्यविन्यास को सामान्य शाब्दिक रूप से बताता है। तो इस समाधान के समान ही है dict(x, **y), नकारात्मक पक्ष के बिना।
कार्ल मेयर

61
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

दोनों शब्दकोशों ('बी') में कुंजियों वाली वस्तुओं के लिए, आप यह नियंत्रित कर सकते हैं कि कौन सा अंतिम में डालकर आउटपुट में समाप्त होता है।


पाइथन 3 में आपको टाइपर्रर मिलेगा: + के लिए असमर्थित ऑपरेंड प्रकार (ओं): 'तानाशाही' और 'तानाशाही' ... आपको सूची के साथ प्रत्येक ताना को संक्षिप्त करना चाहिए (जैसे): तानाशाह (सूची (x.items) ()) सूची (y.items ()))
justSaid

49

जबकि सवाल पहले ही कई बार उत्तर दिया जा चुका है, समस्या का यह सरल समाधान अभी तक सूचीबद्ध नहीं किया गया है।

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

यह z0 और उपर्युक्त बुराई z2 जितना तेज़ है, लेकिन समझने और बदलने में आसान है।


3
लेकिन यह एक व्यंजक के बजाय तीन कथन है
फोरट्रान

14
हाँ! उल्लिखित एक-अभिव्यक्ति-समाधान या तो धीमा या बुरा है। अच्छा कोड पठनीय और बनाए रखने योग्य है। तो समस्या सवाल है जवाब नहीं। हमें एक समस्या का सबसे अच्छा समाधान एक-लाइन-समाधान के लिए नहीं पूछना चाहिए।
फोबी

7
z4 = {}अगली पंक्ति को खोएं और बदलें z4 = x.copy()- केवल अच्छे कोड से बेहतर अनावश्यक चीजें नहीं करता है (जो इसे और भी पठनीय और बनाए रखने योग्य बनाता है)।
मार्टीन्यू

3
आपका सुझाव इसे मैथ्यूज के उत्तर में बदल देगा। हालांकि उनका जवाब ठीक है, मुझे लगता है कि मेरा अधिक पठनीय और बेहतर रखरखाव है। अतिरिक्त लाइन केवल खराब होगी यदि यह निष्पादन समय खर्च करेगा।
फोबी

47
def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

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

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

देता है:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

39

अगर आपको लगता है कि लंबोदर बुरे हैं, तो आगे पढ़ें नहीं। जैसा कि अनुरोध किया गया है, आप एक अभिव्यक्ति के साथ तेज और स्मृति-कुशल समाधान लिख सकते हैं:

x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}

जैसा कि ऊपर बताया गया है, दो पंक्तियों का उपयोग करना या फ़ंक्शन लिखना संभवतः जाने का एक बेहतर तरीका है।


33

पाइथोनिक हो। एक समझ का उपयोग करें :

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

1
एक समारोह के रूप में:def dictmerge(*args): return {i:d[i] for d in args for i in d}
jessexknight

1
सीधे कुंजी / मान जोड़े को पुनरावृत्त करके एक लुकअप को सहेजें:z={k: v for d in (x, y) for k, v in d.items()}
ShadowRanger

30

Python3 में, itemsविधि अब एक सूची नहीं लौटाती है , बल्कि एक दृश्य है , जो एक सेट की तरह काम करता है। इस मामले में आपको +काम नहीं करने के बाद से सेट यूनियन लेने की आवश्यकता होगी:

dict(x.items() | y.items())

संस्करण २.y में पायथन 3-जैसे व्यवहार के लिए, viewitemsविधि को इसके स्थान पर काम करना चाहिए items:

dict(x.viewitems() | y.viewitems())

मैं इस नोटेशन को पसंद करता हूं क्योंकि यह संघटन के बजाय सेट यूनियन ऑपरेशन के रूप में सोचना अधिक स्वाभाविक लगता है (जैसा कि शीर्षक से पता चलता है)।

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

अजगर 3 के लिए कुछ और बिंदु। सबसे पहले, ध्यान दें कि dict(x, **y)चाल अजगर 3 में काम नहीं करेगी जब तक कि इसमें चाबी नहींy तार नहीं हैं।

इसके अलावा, रेमंड हेटिंगर के चैनमैप का जवाब बहुत ही सुंदर है, क्योंकि यह तर्कों के रूप में कई मनमाने ढंग से नंबर ले सकता है, लेकिन डॉक्स से ऐसा लगता है कि यह क्रमिक रूप से प्रत्येक लुकअप के लिए सभी डीकट्स की सूची के माध्यम से दिखता है:

जब तक कोई कुंजी नहीं मिलती तब तक लुकअप अंतर्निहित मैपिंग को क्रमिक रूप से खोजता है।

यदि आपके एप्लिकेशन में बहुत सारे लुकअप हैं, तो यह आपको धीमा कर सकता है:

In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop

लुकअप के लिए परिमाण धीमे के क्रम के बारे में। मैं चैनमैप का प्रशंसक हूं, लेकिन कम व्यावहारिक दिखता है जहां कई लुकअप हो सकते हैं।


22

मैथ्यू के जवाब के लिए एक अभिव्यक्ति समाधान के लिए अग्रणी दुरुपयोग :

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (lambda f=x.copy(): (f.update(y), f)[1])()
>>> z
{'a': 1, 'c': 11, 'b': 10}

आपने कहा कि आप एक अभिव्यक्ति चाहते हैं, इसलिए मैंने lambdaएक नाम को बांधने के लिए दुर्व्यवहार किया, और लांबा की एक-अभिव्यक्ति की सीमा को पार करने के लिए ट्यूपल्स का इस्तेमाल किया। झंझट से मुक्त होना।

यदि आप इसकी नकल करने से गुरेज नहीं करते हैं, तो आप यह भी कर सकते हैं:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}

22

आदेश का संरक्षण करने वाले इटर्टूल का उपयोग करते हुए सरल समाधान (बाद वाले डीकट्स में पूर्वता है)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

और यह उपयोग है:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}


16

भले ही इस उथले शब्दकोश के लिए उत्तर अच्छे थे , लेकिन यहां परिभाषित तरीकों में से कोई भी वास्तव में एक गहन शब्दकोश मर्ज नहीं करता है।

उदाहरण निम्नलिखित हैं:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

कुछ इस तरह से परिणाम की उम्मीद करेंगे:

{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }

इसके बजाय, हमें यह मिलता है:

{'two': True, 'one': {'extra': False}}

'एक' एंट्री में 'डिफेन्स_2' और 'एक्सट्रा' में इसके शब्दकोश के अंदर मौजूद आइटम्स होने चाहिए, अगर यह वास्तव में मर्ज था।

श्रृंखला का उपयोग करना भी, काम नहीं करता है:

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

का परिणाम:

{'two': True, 'one': {'extra': False}}

Rcwesick ने जो गहरा मर्ज दिया, वह भी वही परिणाम पैदा करता है।

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


11

(केवल Python2.7 * के लिए; पायथन 3 * के लिए सरल उपाय हैं।)

यदि आप एक मानक पुस्तकालय मॉड्यूल आयात करने से पीछे नहीं हैं, तो आप कर सकते हैं

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

(इसमें or aबिट lambdaआवश्यक है क्योंकि dict.updateहमेशा Noneसफलता मिलती है।)


11

यदि आपको मन मुटाव नहीं है x,

x.update(y) or x

सरल, पठनीय, प्रदर्शन करने वाला। आपको पता है कि update() हमेशा रिटर्न होता है None, जो गलत मूल्य है। इसलिए xअद्यतन करने के बाद, उपरोक्त अभिव्यक्ति हमेशा मूल्यांकन करेगी ।

मानक पुस्तकालय (जैसे .update()) में म्यूटिंग तरीके Noneसम्मेलन द्वारा लौटते हैं , इसलिए यह पैटर्न उन पर भी काम करेगा। यदि आप एक ऐसी विधि का उपयोग कर रहे हैं जो इस सम्मेलन का पालन नहीं करती है, तो orकाम नहीं कर सकता है। लेकिन, आप इसके बजाय एक एकल अभिव्यक्ति बनाने के लिए एक टपल डिस्प्ले और इंडेक्स का उपयोग कर सकते हैं। यह इस बात पर ध्यान दिए बिना काम करता है कि पहला तत्व किसका मूल्यांकन करता है।

(x.update(y), x)[-1]

यदि आपके पास xअभी तक एक चर में नहीं है, तो आप lambdaअसाइनमेंट स्टेटमेंट का उपयोग किए बिना स्थानीय बनाने के लिए उपयोग कर सकते हैं । यह lambdaएक अभिव्यक्ति के रूप में उपयोग करने के लिए है , जो कार्यात्मक भाषाओं में एक सामान्य तकनीक है, लेकिन शायद अनहोनी।

(lambda x: x.update(y) or x)({'a': 1, 'b': 2})

हालाँकि यह नया वालरस ऑपरेटर (पायथन 3.8+ केवल) के निम्नलिखित उपयोग से अलग नहीं है:

(x := {'a': 1, 'b': 2}).update(y) or x

यदि आप एक प्रति चाहते हैं, तो PEP 448 शैली सबसे आसान है {**x, **y}। लेकिन अगर यह आपके (पुराने) पायथन संस्करण में उपलब्ध नहीं है, तो लेट पैटर्न यहां भी काम करता है।

(lambda z: z.update(y) or z)(x.copy())

(यह, ज़ाहिर है, के बराबर है (z := x.copy()).update(y) or z, लेकिन अगर आपका पायथन संस्करण उसके लिए काफी नया है, तो पीईपी 448 शैली उपलब्ध होगी।)


10

यहाँ और कहीं और विचारों पर आकर्षित मैंने एक समारोह की रचना की है:

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

उपयोग (अजगर 3 में परीक्षण):

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

आप इसके बजाय एक लैम्ब्डा का उपयोग कर सकते हैं।


10

मेरे पास आज तक सूचीबद्ध समाधानों के साथ समस्या यह है कि, मर्ज किए गए शब्दकोश में, कुंजी "बी" का मूल्य 10 है लेकिन, मेरे सोचने के तरीके के लिए, यह 12 होना चाहिए। उस प्रकाश में, मैं निम्नलिखित प्रस्तुत करता हूं:

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

परिणाम:

0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}

1
आप में रुचि हो सकती है cytoolz.merge_with( toolz.readthedocs.io/en/latest/… )
bli

10

यह इतना मूर्खतापूर्ण है कि .updateकुछ भी नहीं लौटाता है।
मैं सिर्फ समस्या को हल करने के लिए एक साधारण सहायक फ़ंक्शन का उपयोग करता हूं:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

उदाहरण:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

10
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

इससे आपकी समस्या का समाधान हो जाना चाहिए।


9

यह एक एकल समझ के साथ किया जा सकता है:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> { key: y[key] if key in y else x[key]
      for key in set(x) + set(y)
    }

मेरे विचार में 'सिंगल एक्सप्रेशन' वाले हिस्से के लिए सबसे अच्छा उत्तर, क्योंकि अतिरिक्त कार्यों की आवश्यकता नहीं है, और यह छोटा है।


मुझे संदेह है कि प्रदर्शन बहुत अच्छा नहीं होगा; प्रत्येक तानाशाही से एक सेट बनाने के बाद, केवल कुंजियों के माध्यम से पुनरावृत्ति करने का मतलब है कि हर बार मूल्य के लिए एक और खोज (हालांकि अपेक्षाकृत तेज़, अभी भी स्केलिंग के लिए फ़ंक्शन का क्रम बढ़ जाता है)
ब्रीज़र

2
यह सब उस अजगर के संस्करण पर निर्भर करता है जिसका हम उपयोग कर रहे हैं। 3.5 और ऊपर {** x, ** y} संक्षिप्त शब्द देता है
राशिद Mv

9

एक नया विकल्प होगा जब पायथन 3.8 रिलीज़ ( 20 अक्टूबर, 2019 के लिए निर्धारित ), पीईपी 572 के लिए धन्यवाद : असाइनमेंट एक्सप्रेशंस । नया असाइनमेंट अभिव्यक्ति ऑपरेटर :=आपको परिणाम को असाइन करने की अनुमति देता है copyऔर फिर भी कॉल करने के लिए इसका उपयोग करता है update, संयुक्त कोड को दो बयानों के बजाय एक एकल अभिव्यक्ति छोड़कर, बदल रहा है:

newdict = dict1.copy()
newdict.update(dict2)

सेवा:

(newdict := dict1.copy()).update(dict2)

हर तरह से व्यवहार करते हुए। यदि आपको परिणाम भी लौटाना चाहिए dict(आप ने वापसी के लिए एक अभिव्यक्ति मांगी dict, तो उपरोक्त बनाता है और उसे असाइन करता है newdict, लेकिन इसे वापस नहीं करता है, इसलिए आप इसका उपयोग किसी फ़ंक्शन के तर्क को पास करने के लिए नहीं कर सकते हैं, एक ला myfunc((newdict := dict1.copy()).update(dict2))) , तो बस or newdictअंत में जोड़ें (चूंकि updateरिटर्न None, जो गलत है, यह तब मूल्यांकन करेगा और newdictअभिव्यक्ति के परिणाम के रूप में वापस आ जाएगा ):

(newdict := dict1.copy()).update(dict2) or newdict

महत्वपूर्ण चेतावनी: सामान्य तौर पर, मैं इस दृष्टिकोण को इसके पक्ष में हतोत्साहित करूंगा:

newdict = {**dict1, **dict2}

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

newdict = {}
newdict.update(dict1)
newdict.update(dict2)

लेकिन ठोस dictAPI का उपयोग करते हुए C लेयर पर किया जाता है, इसलिए कोई डायनेमिक विधि लुकअप / बाइंडिंग या फ़ंक्शन कॉल डिस्पैच ओवरहेड शामिल नहीं होता है -(newdict := dict1.copy()).update(dict2) व्यवहार में मूल दो-लाइनर के समान अपरिहार्य है, डायनेमिक लुकअप के साथ असतत चरणों में कार्य करना, / बाध्यकारी / तरीकों का आह्वान।

यह तीन गुना अधिक dictस्पष्ट है , क्योंकि यह अधिक एक्सटेंसिबल है:

 newdict = {**dict1, **dict2, **dict3}

जहां असाइनमेंट एक्सप्रेशन का उपयोग करते हुए उस तरह स्केल नहीं होगा; निकटतम तुम हो सकता है:

 (newdict := dict1.copy()).update(dict2), newdict.update(dict3)

या अस्थायी टपल के बिना None, लेकिन प्रत्येक Noneपरिणाम की सत्यता परीक्षण के साथ :

 (newdict := dict1.copy()).update(dict2) or newdict.update(dict3)

या तो जिनमें से स्पष्ट रूप से बहुत भद्दा है, और आगे अक्षमताओं भी शामिल है (या तो एक व्यर्थ अस्थायी tupleकी Noneअल्पविराम जुदाई, या प्रत्येक के व्यर्थ truthiness परीक्षण के लिए रों updateके Noneलिए बदले orजुदाई)।

असाइनमेंट एक्सप्रेशन एप्रोच का एकमात्र वास्तविक लाभ होता है यदि:

  1. आपके पास जेनेरिक कोड है, जो sets और dicts दोनों को संभालता है (दोनों का समर्थन copyऔर update, इसलिए कोड लगभग उतना ही काम करता है जितना आप इसकी अपेक्षा करते हैं)
  2. आप मनमाने ढंग से तानाशाही जैसी वस्तुएं प्राप्त करने की अपेक्षा करते हैं , न केवल dictस्वयं, और बाएं हाथ की ओर के प्रकार और शब्दार्थ को संरक्षित करना चाहिए (बल्कि एक सादे के साथ समाप्त होने के बजाय dict)। जबकि myspecialdict({**speciala, **specialb})काम हो सकता है, इसमें एक अतिरिक्त अस्थायी शामिल होगा dict, और अगर myspecialdictफीचर्स प्लेन dictको संरक्षित नहीं कर सकते हैं (जैसे नियमित dictएस अब एक कुंजी की पहली उपस्थिति के आधार पर आदेश को संरक्षित करते हैं, और एक कुंजी के अंतिम स्वरूप के आधार पर मूल्य; आप चाहते हो सकते हैं; अंतिम के आधार पर आदेश को संरक्षित करने वाला कुंजी उपस्थिति के इसलिए एक मूल्य को अपडेट करना भी इसे अंत तक ले जाता है), फिर शब्दार्थ गलत होगा। चूंकि असाइनमेंट एक्सप्रेशन संस्करण नामित विधियों का उपयोग करता है (जो उचित रूप से उचित रूप से व्यवहार करने के लिए अतिभारित होता है), यह कभी नहीं बनाता हैdictसभी पर (जब तक dict1कि पहले से ही dict), मूल प्रकार (और मूल प्रकार के शब्दार्थ) को संरक्षित करते हुए, सभी किसी भी अस्थायी से बचते हुए।

8
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}

यह विधि xइसकी प्रति के साथ ओवरराइट करती है। यदि xएक फ़ंक्शन तर्क है तो यह काम नहीं करेगा ( उदाहरण देखें )
बार्टोलो-ओटिट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.