python - كيفية دمج قواميسين في تعبير واحد؟




dictionary merge (25)

متكرر / عميق تحديث dict

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}

المخرجات:

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

شكرا rednaw عن التعديلات.

لدي قواميس Python اثنين ، وأريد كتابة تعبير واحد يعيد هاتين القارمتين ، دمج. سيكون أسلوب 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}

كيف يمكنني الحصول على هذا dict النهائي المدمجة في z ، وليس x ؟

(لكي تكون أكثر وضوحًا ، فإن الفوز الأخير في الصراع على dict.update() هو ما أبحث عنه أيضًا.)


في إجابة المتابعة ، سألت عن الأداء النسبي لهذين الخيارين:

% 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

على الجهاز الخاص بي ، على الأقل (تشغيل x86_64 عادية إلى حد ما x البيوتون 2.5.2) ، فإن z2 البديل ليس أقصر وأبسط فحسب بل أيضًا بشكل أسرع. يمكنك التحقق من ذلك بنفسك باستخدام وحدة timeit التي تأتي مع Python.

المثال 1: قواميس متطابقة تعين 20 أعدادًا متتالية من الأعداد الصحيحة لنفسها:

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

يفوز z2 بعامل قدره 3.5 أو نحو ذلك. يبدو أن القواميس المختلفة تعطي نتائج مختلفة تمامًا ، ولكن يبدو أن z2 دائمًا ما يظهر في المستقبل. (إذا حصلت على نتائج غير متسقة للاختبار نفسه ، فجرّب تمرير -r برقم أكبر من الرقم الافتراضي 3.)

مثال 2: قواميس غير متراكبة تقوم بتعيين 252 سلسلة قصيرة إلى الأعداد الصحيحة والعكس صحيح:

% 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

يفوز z2 بنحو عامل 10. إنه فوز كبير في كتابي!

بعد مقارنة هذين ، تساءلت عما إذا كان الأداء الضعيف z1 يمكن أن يعزى إلى النفقات العامة لبناء قائمتين من العناصر ، مما دفعني إلى التساؤل عما إذا كان هذا التباين قد يعمل بشكل أفضل:

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

بعض الاختبارات السريعة ، على سبيل المثال

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

يؤدي بي إلى استنتاج أن z3 أسرع إلى حد ما من z1 ، ولكن ليس بنفس سرعة z2 . بالتأكيد لا يستحق كل الكتابة الإضافية.

لا تزال هذه المناقشة مفقودة شيء مهم ، وهو مقارنة أداء هذه البدائل بالطريقة "الواضحة" لدمج قائمتين: استخدام طريقة update . لمحاولة إبقاء الأمور على قدم المساواة مع التعبيرات ، لا يعدل أي منها x أو y ، سأقوم بعمل نسخة x بدلاً من تعديله في الموضع ، كما يلي:

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

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

نتيجة نموذجية:

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

بمعنى آخر ، يبدو أن z0 و z2 لها أداء متطابق في الأساس. هل تعتقد أن هذا قد يكون مجرد مصادفة؟ انا لا....

في الواقع ، أود أن أذهب إلى حد الادعاء بأنه من المستحيل على بيثون نقية أن تفعل أي شيء أفضل من ذلك. وإذا كان بإمكانك القيام بعمل أفضل بكثير في وحدة C للإرشاد ، فإنني أتخيل أن أفراد بايثون قد يكونون مهتمين بإدماج الكود الخاص بك (أو شكل مختلف في منهجك) في نواة بايثون. بايثون تستخدم طريقة في العديد من الأماكن ؛ تحسين عملياتها هو صفقة كبيرة.

يمكنك أيضا كتابة هذا باسم

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

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

print deepupdate(pluto_original, pluto_update)

كما يفعل توني ، ولكن (ليس من المستغرب) الفرق في الترميز تبين أنه ليس له أي تأثير قابل للقياس على الأداء. استخدم أيهما يبدو لك بشكل صحيح. وبالطبع ، فإنه من الصحيح تمامًا أن يشير إلى أن إصدار البيانين أسهل بكثير للفهم.


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

هذا يجب ان يحل مشكلتك.


على الرغم من أن الإجابات كانت جيدة لهذا القاموس الضحل ، إلا أن أيا من الأساليب المعرفة هنا لا تقوم بالفعل بدمج معجم عميق.

اتبع الأمثلة:

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

يتوقع المرء نتيجة لشيء من هذا القبيل:

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

بدلاً من ذلك ، نحصل على هذا:

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

يجب أن يكون الإدخال "one" قد يكون "depth_2" و "extra" كعناصر داخل قاموسه إذا كان بالفعل دمجًا.

باستخدام سلسلة أيضا ، لا يعمل:

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

النتائج في:

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

الدمج العميق الذي قدمه rcwesick يخلق نفس النتيجة.

نعم ، ستعمل على دمج القواميس النموذجية ، ولكن لا يعتبر أي منها آلية عامة للدمج. سأقوم بتحديث هذا في وقت لاحق عندما أكتب طريقة تقوم بدمج صحيح.



ربما لن تكون هذه إجابة شائعة ، لكنك بالتأكيد لا تريد القيام بذلك. إذا كنت ترغب في نسخة يتم دمجها ، فاستخدم النسخة (أو deepcopy ، حسب ما تريد) ثم قم بتحديثه. سطران من التعليمات البرمجية أكثر قابلية للقراءة - أكثر Pythonic - من إنشاء سطر مفرد مع .items () + .items (). صريح أفضل من الضمني.

بالإضافة إلى ذلك ، عند استخدام .items () (قبل Python 3.0) ، تقوم بإنشاء قائمة جديدة تحتوي على العناصر من dict. إذا كانت القواميس الخاصة بك كبيرة ، فذلك هو الكثير من النفقات العامة (قاعدتان كبيرتان سيتم طرحهما بمجرد إنشاء dict المدمجة). يمكن أن يعمل التحديث () بشكل أكثر كفاءة ، لأنه يمكن تشغيله من خلال البند الثاني لكل عنصر.

من حيث time :

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

IMO التباطؤ الصغير بين الأولين يستحق ذلك للقراءة. بالإضافة إلى ذلك ، تمت إضافة وسائط الكلمات الرئيسية لإنشاء القاموس في Python 2.3 فقط ، بينما ستعمل نسخة () و update () في الإصدارات الأقدم.


لبيثون 2:

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

لبيثون 3:

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

يعطي الناتج: {'a': 1, 'c': 11, 'b': 10}


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

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

من بين هذه الإجابات المشبوهة والمريبة ، فإن هذا المثال المشرق هو الطريقة الوحيدة والصالحة الوحيدة لدمج الدايكل في بايثون ، التي أيدها الدكتاتور مدى الحياة ، غويدو فان روسوم نفسه! اقترح شخص آخر نصف هذا ، لكنه لم يضعه في وظيفة.

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

يعطي:

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

كيف يمكنني دمج قواميس Python في تعبير واحد؟

بالنسبة للقواميس x و y ، يتحول z إلى قاموس مدمج ضحلًا بقيم من y تحل محل تلك من x .

  • في Python 3.5 أو أكبر:

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • في Python 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)
    

تفسير

لنفترض أن لديك اثنين من dicts وتريد دمجها في dict جديد دون تغيير dicts الأصلي:

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

النتيجة المطلوبة هي الحصول على قاموس جديد ( z ) مع دمج القيم ، وقيم dict الثانية التي تكتبها من الأولى.

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

بناء جملة جديدة لهذا ، المقترح في PEP 448 ومتاح من Python 3.5 ، هو

z = {**x, **y}

وهو بالفعل تعبير واحد.

لاحظ أنه يمكننا الدمج مع التدوين الحرفي أيضًا:

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

و الأن:

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

يظهر الآن كما تم تنفيذه في جدول الإصدار لـ 3.5 ، PEP 478 ، وقد أصبح الآن في طريقه إلى ما هو الجديد في وثيقة Python 3.5 .

ومع ذلك ، بما أن العديد من المؤسسات لا تزال في Python 2 ، فقد ترغب في القيام بذلك بطريقة متوافقة مع الخلف. الطريقة الكلاسيكية الكلاسيكية ، المتوفرة في Python 2 و Python 3.0-3.4 ، هي القيام بذلك كخطوة من خطوتين:

z = merge_two_dicts(x, y)

في كلا الأسلوبين ، سوف يأتي y المرتبة الثانية وقيمها ستحل محل قيم x ، وبالتالي سوف تشير 'b' إلى 3 في النتيجة النهائية.

ليس بعد على Python 3.5 ، ولكن تريد تعبيرًا واحدًا

إذا لم تكن بعد على Python 3.5 ، أو تحتاج إلى كتابة التعليمات البرمجية المتوافقة مع السابقة ، وترغب في ذلك في تعبير واحد ، يكون الأسلوب الأكثر أداءً بينما الأسلوب الصحيح هو وضعه في إحدى الوظائف:

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

ثم لديك تعبير واحد:

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

يمكنك أيضًا إنشاء وظيفة لدمج عدد غير محدد من dicts ، من صفر إلى رقم كبير جدًا:

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

ستعمل هذه الوظيفة في Python 2 و 3 لجميع dicts. على سبيل المثال dicts a to g :

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

وأزواج القيم الأساسية في g سيكون لها الأسبقية على dicts a إلى f ، وهكذا.

النقد من أجوبة أخرى

لا تستخدم ما تراه في الإجابة المقبولة سابقًا:

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

في Python 2 ، يمكنك إنشاء قائمتين في الذاكرة لكل dict ، وإنشاء قائمة ثالثة في الذاكرة بطول يساوي طول أول نقطتين معاً ، ثم تجاهل كل القوائم الثلاثة لإنشاء الأمر dict. في Python 3 ، سيفشل هذا لأنك تضيف كائنين dict_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'

وسيكون عليك إنشاءها صراحةً كقوائم ، على سبيل المثال z = dict(list(x.items()) + list(y.items())) . هذا مضيعة للموارد وقوة الحساب.

وبالمثل ، فإن أخذ اتحاد items() في Python 3 ( viewitems() في Python 2.7 سوف يفشل أيضًا عندما تكون القيم كائنات غير قابلة للتلف (مثل القوائم ، على سبيل المثال). حتى إذا كانت قيمك قابلة للغسل ، بما أن المجموعات غير مرسومة بشكل غير لغوي ، فإن السلوك غير محدد فيما يتعلق بالأسبقية. لذلك لا تفعل هذا:

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

يوضح هذا المثال ما يحدث عندما تكون القيم غير قابلة للفساد:

z = dict(x, **y)

في ما يلي مثال حيث يجب أن يكون لـ y الأسبقية ، ولكن بدلاً من ذلك يتم الاحتفاظ بالقيمة من x بسبب الترتيب التعسفي للمجموعات:

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

اختراق آخر لا يجب عليك استخدامه:

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

يستخدم هذا المُنشئ dict ، وهو سريع للغاية وذاكرة فعالة (حتى أكثر قليلاً من عملية خطوتين) ولكن ما لم تعرف بالضبط ما يحدث هنا (أي ، يتم تمرير الأمر الثاني كوسيطة للكلمات الرئيسية dict constructor) ، يصعب قراءتها ، وليس الاستخدام المقصود ، وبالتالي فهي ليست Pythonic.

في ما يلي مثال على الاستخدام الذي تم إصلاحه في django .

تم تصميم Dicts لأخذ مفاتيح قابلة للغسل (مثل frozensets أو tuples) ، ولكن هذه الطريقة تفشل في Python 3 عندما لا تكون المفاتيح سلاسل.

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

من القائمة البريدية ، كتب جويدو فان روسوم ، مبتكر اللغة ، ما يلي:

أنا على ما يرام مع إعلان dict ({} ، ** {1: 3}) غير قانوني ، لأنه بعد كل شيء هو إساءة استخدام ** آلية.

و

يبدو يملي (س ، ** ذ) يدور حول "بارد الإختراق" ل "استدعاء x.update (ذ) والعودة س". أنا شخصياً أجده أكثر دهاءًا من رائع.

أفهم (بالإضافة إلى فهم منشئ اللغة ) أن الاستخدام المقصود من أجل dict(**y) هو لإنشاء dicts لأغراض القراءة ، على سبيل المثال:

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

بدلا من

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

الرد على التعليقات

على الرغم من ما يقوله غيدو ، فإن dict(x, **y) يتماشى مع مواصفات ال dict ، التي وقعت. يعمل لكل من Python 2 و 3. إن حقيقة أن هذا يعمل فقط لمفاتيح السلسلة هو نتيجة مباشرة لكيفية عمل معلمات الكلمات الرئيسية وليس قلة قصيرة من dict. ولا يستخدم المشغل ** في هذا المكان سوء استخدام للآلية ، في الواقع ** تم تصميمه بدقة لتمرير dicts ككلمات رئيسية.

مرة أخرى ، فإنه لا يعمل لمدة 3 عندما تكون المفاتيح غير سلاسل. عقد الاستدعاء الضمني هو أن مساحات الأسماء تأخذ dicts عادية ، بينما يجب أن يقوم المستخدمون فقط بتمرير الوسيطات الرئيسية التي هي سلاسل. جميع المتصلين الآخرين بفرضها. اخترق هذا الاتساق في بيثون 2:

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

هذا التناقض كان سيئا نظرا للتطبيقات الأخرى لبيثون (Pypy، Jython، IronPython). وهكذا تم تثبيته في Python 3 ، لأن هذا الاستخدام يمكن أن يكون تغييرًا مفاجئًا.

أقدم لك أنه من عدم الكفاءة الخبيثة لتعمد كتابة التعليمات البرمجية التي تعمل فقط في إصدار واحد من لغة أو لا تعمل إلا في ظل قيود تعسفية معينة.

المزيد من التعليقات:

dict(x.items() + y.items()) الحل الأكثر قابلية للقراءة لـ Python 2. قابلية القراءة.

merge_two_dicts(x, y) : merge_two_dicts(x, y) يبدو أكثر وضوحًا لي ، إذا كنا قلقين بالفعل بشأن قابلية القراءة. وهي غير متوافقة ، حيث يتم إهمال Python 2 بشكل متزايد.

لا يبدو أن {**x, **y} يتعامل مع القواميس المتداخلة. يتم ببساطة الكتابة فوق محتويات المفاتيح المتداخلة ، وليس دمجها [...] انتهى بي الأمر إلى أن أحرق من خلال هذه الإجابات التي لا تندمج بشكل متكرر وفوجئت لم يذكرها أحد. في تفسيراتي لكلمة "دمج" ، تصف هذه الإجابات "تحديث أمر واحد بآخر" ، وليس الدمج.

نعم فعلا. يجب أن أعيدك إلى السؤال الذي يطالب بدمج ضحل بين قواميسين ، مع كتابة قيم الأول من خلال قيم الثانية - في تعبير واحد.

بافتراض قاموسين للقواميس ، يمكن دمجها بشكل متكرر في وظيفة واحدة ، ولكن يجب أن تكون حريصًا على عدم تعديل dicts من أي من المصدرين ، والطريقة الأكثر أملاً لتجنب ذلك هي عمل نسخة عند تعيين قيم. نظرًا لأن المفاتيح يجب أن تكون قابلة للغسيل وتكون غير قابلة للتغيير دائمًا ، فلا جدوى من نسخها:

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

الاستعمال:

import timeit

إن الحديث مع الحالات الطارئة لأنواع القيم الأخرى هو أبعد من نطاق هذا السؤال ، لذلك سأوجهك إلى إجابتي على السؤال القانوني حول "قواميس دمج القواميس" .

أقل أداء ولكن صحيح - Ad-hocs

هذه الأساليب أقل أداء ، لكنها ستوفر السلوك الصحيح. سيكونون أقل أداء بكثير من copy update أو التفريغ الجديد لأنهم يتكررون من خلال كل زوج من القيم الرئيسية عند مستوى أعلى من التجريد ، لكنهم يحترمون ترتيب الأسبقية (الأسبقيات الأخيرة لها الأسبقية)

يمكنك أيضًا تجميع الدايونات يدويًا داخل الفهم الإملائي:

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

أو في python 2.6 (وربما في وقت مبكر من 2.4 عندما تم إدخال تعبيرات المولد):

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

سيقوم itertools.chain بسلسلة itertools.chain على أزواج القيم الرئيسية بالترتيب الصحيح:

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

تحليل الأداء

سأفعل فقط تحليل أداء الأعراف المعروفة بالتصرف بشكل صحيح.

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

ما يلي هو على أوبونتو 14.04

في Python 2.7 (نظام Python):

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

في Python 3.5 (Deadsnakes PPA):

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

الموارد على القواميس


في python3 ، لم تعد طريقة items ترجع القائمة ، بل إنها طريقة عرض ، تعمل كمجموعة. في هذه الحالة ، ستحتاج إلى اتخاذ الاتحاد المحدد نظرًا لأن الجمع مع + لن يعمل:

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

بالنسبة لسلوك python3-like في الإصدار 2.7 ، يجب أن تعمل طريقة viewitems بدلاً من items :

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

أنا أفضل هذا الترميز على أي حال لأنه يبدو أكثر طبيعية أن نفكر فيه كعملية اتحاد محددة بدلاً من التسلسل (كما يظهر العنوان).

تصحيح:

نقطتان إضافيتان للثعبان 3. أولاً ، لاحظ أن الخدعة dict(x, **y) لن تعمل في python 3 إلا إذا كانت المفاتيح في y هي سلاسل.

أيضا ، answer رنمين هيتينجر في Chainmap هي أنيقة جدا ، لأنها يمكن أن تأخذ عددا من الأحكام dicts تعسفية مثل الحجج ، ولكن من مستندات يبدو أنها تبدو بالتسلسل من خلال قائمة من جميع dicts لكل بحث:

تبحث عمليات البحث عن التعيينات الأساسية على التوالي حتى يتم العثور على مفتاح.

قد يؤدي ذلك إلى إبطاء عملك إذا كان لديك الكثير من عمليات البحث في التطبيق الخاص بك:

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

لذلك حول ترتيب من حجم أبطأ لعمليات البحث. أنا من المعجبين بـ Chainmap ، ولكن يبدو أقل عملية حيث قد يكون هناك العديد من عمليات البحث.


يمكن القيام بذلك من خلال فهم مفرد:

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

في رأيي ، هناك حاجة إلى أفضل إجابة لجزء "التعبير الواحد" حيث لا توجد وظائف إضافية ، وهو قصير.


إذا كنت تعتقد أن lambdas هي الشر ثم لا تقرأ. كما هو مطلوب ، يمكنك كتابة الحل السريع والفعال للذاكرة باستخدام تعبير واحد:

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

كما هو موضح أعلاه ، من المحتمل أن يكون استخدام خطين أو كتابة وظيفة طريقة أفضل للانطلاق.


المشكلة التي لدي مع الحلول المدرجة حتى الآن هي أنه ، في القاموس المدمج ، تكون قيمة المفتاح "ب" 10 ، ولكن إلى طريقة تفكيري ، يجب أن تكون 12. في ضوء ذلك ، أقدم ما يلي:

from functools import reduce

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

النتائج:

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}

Python 3.5 (PEP 448) يسمح بخيار تركيب أجمل:

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

إساءة الاستخدام مما يؤدي إلى حل واحد للتعبير عن إجابة ماثيو :

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

لقد قلت أنك تريد تعبيرًا واحدًا ، لذا أساءت إلى lambda لربط اسمًا ، وحجمًا لتجاوز حد التعبير الوحيد لـ lambda. لا تتردد في تذلل.

يمكنك أيضًا القيام بذلك بالطبع إذا كنت لا تهتم بنسخه:

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

خيار آخر أكثر إيجازًا:

% 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

ملاحظة : لقد أصبح هذا جوابًا شائعًا ، ولكن من المهم الإشارة إلى أنه إذا كان لدى أي مفاتيح غير خيطية ، فإن حقيقة أن هذا يعمل على الإطلاق هو إساءة استخدام لتفاصيل تطبيق CPython ، ولا يعمل في Python 3. أو في PyPy أو IronPython أو Jython. أيضا ، غيدو ليس من المعجبين . لذلك لا يمكنني أن أوصي بهذه التقنية للحصول على كود محمول متوافق أو متوافق مع التطبيق ، وهذا يعني أنه يجب تجنبه تمامًا.


كنت أرغب في شيء مشابه ، ولكن مع القدرة على تحديد كيفية دمج القيم على مفاتيح مكررة ، لذلك اخترقت هذا (ولكن لم تختبره بكثافة). من الواضح أن هذا ليس تعبيرًا واحدًا ، ولكنه عبارة عن مكالمة مفردة.

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

في حالتك ، ما يمكنك القيام به هو:

z = dict(x, **y)

سيقوم هذا ، كما تريد ، بوضع الأمر الأخير في z ، وجعل قيمة المفتاح b يتم تجاوزها بشكل صحيح بواسطة قيمة dict الثانية ( y ):

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

إذا كنت تستخدم Python 3 ، فستكون أكثر تعقيدًا. لإنشاء z :

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

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

أفضل إصدار يمكن أن أفكر فيه أثناء عدم استخدام النسخة هو:

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

إنه أسرع من dict(x.items() + y.items()) ولكن ليس بالسرعة مثل n = copy(a); n.update(b) n = copy(a); n.update(b) ، على الأقل في CPython. يعمل هذا الإصدار أيضًا في Python 3 إذا قمت بتغيير iteritems() إلى items() ، والذي يتم إجراؤه تلقائيًا بواسطة أداة 2to3.

أنا شخصياً أحب هذا الإصدار أفضل لأنه يصف جيدًا ما أريده في بناء جملة وظيفي واحد. المشكلة البسيطة الوحيدة هي أنه لا يجعل من الواضح تماما أن القيم من ذ تأخذ الأسبقية على القيم من س ، ولكن لا أعتقد أنه من الصعب معرفة ذلك.


بالاعتماد على الأفكار هنا وفي أي مكان آخر ، استوعبت وظيفة:

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}

الاستخدام (تم اختباره في python 3):

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

يمكنك استخدام لامدا بدلاً من ذلك.


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

بالنسبة للعناصر التي تحتوي على مفاتيح في كل من القواميس ('b') ، يمكنك التحكم في أيٍّ منها ينتهي بالإخراج بوضع ذلك الأخير.


في حين تم الإجابة على السؤال عدة مرات ، لم يتم سرد هذا الحل البسيط للمشكلة حتى الآن.

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}

إنه سريع مثل z0 و z2 الشر المذكورة أعلاه ، ولكن من السهل فهم والتغيير.


حل بسيط باستخدام itertools الذي يحافظ على النظام (الأسبقيات الأخيرة لها الأسبقية)

import collections
a = {1: 1, 2: 2}
b = {2: 3, 3: 4}
c = {3: 5}

r = dict(collections.ChainMap(a, b, c))
print(r)

وانها الاستخدام:

{1: 1, 2: 2, 3: 4}

للتكرار أكثر من المفاتيح ، يكون أبطأ ولكن من الأفضل استخدام my_dict.keys() . إذا حاولت فعل شيء كالتالي:

for key in my_dict:
    my_dict[key+"-1"] = my_dict[key]-1

فإنه ينشئ خطأ وقت التشغيل لأنك تقوم بتغيير المفاتيح أثناء تشغيل البرنامج. إذا كنت for key in my_dict تمامًا لخفض الوقت ، فاستخدم for key in my_dict طريقة for key in my_dict ، ولكن تم تحذيرك ؛).





python dictionary merge