Vra

Ek het twee Python woordeboeke, en ek wil om te skryf'n enkele uitdrukking dat die opbrengs van hierdie twee woordeboeke, saamgesmelt.Die update() metode sou wees wat ek nodig het, as dit teruggestuur sy gevolg in plaas van die wysiging van'n dict in-plek.

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

Hoe kan ek kry dat die finale saamgesmelt foutief in z, nie x?

(Om te wees ekstra-duidelik, die laaste-een-wen konflik-hantering van dict.update() is wat ek is op soek na so goed.)

Was dit nuttig?

Oplossing

  

Hoe kan ek saamsmelt twee Python woordeboeke in 'n enkele uitdrukking?

Vir woordeboeke x en y, z word 'n vlak saamgesmelt woordeboek met waardes van y vervang dié van x.

  • In Python 3.5 of groter:

    z = {**x, **y}
    
  • In Python 2, (of 3.4 of laer) skryf 'n funksie:

    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
    

    en nou:

    z = merge_two_dicts(x, y)
    

Verduideliking

Sê jy twee dicts en jy wil hulle saamsmelt in 'n nuwe dict sonder om die oorspronklike dicts:

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

Die gewenste resultaat is 'n nuwe woordeboek (z) kry met die waardes saamgesmelt, en waardes die tweede dict se vervang dié van die eerste.

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

'n Nuwe sintaksis vir hierdie, in PEP 448 voorgestel en beskikbaar as van 'n afgestorwene 3.5 , is

z = {**x, **y}

En dit is inderdaad 'n enkele uitdrukking.

Let daarop dat ons kan saamsmelt in met letterlike notasie sowel:

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

en nou:

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

Dit is nou wat as geïmplementeer in die release skedule vir 3.5, PEP 478 , en dit het nou sy pad na Wat is nuut in Python 3.5 dokument.

Maar, aangesien baie organisasies is steeds op Python 2, jy mag wens om dit te doen in 'n agteruit verenigbaar manier. Die klassiek Pythonic manier, beskikbaar in Python 2 en Python 3,0-3,4, is om dit te doen as 'n twee-stap proses:

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

In beide benaderings, sal y tweede kom en sy waardes sal waardes x se vervang, dus 'b' sal verwys na 3 in ons finale uitslag.

Nog nie op Python 3.5, maar wil 'n enkele uitdrukking

As jy nog nie op Python 3.5 of nodig het om agteruit-versoenbare kode te skryf, en jy wil dit in 'n enkele uitdrukking , die mees performante terwyl korrekte benadering is om dit in 'n funksie te sit :

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

en dan moet jy 'n enkele uitdrukking:

z = merge_two_dicts(x, y)

Jy kan ook 'n funksie te maak om 'n ongedefinieerde aantal dicts saamsmelt, van nul tot 'n baie groot aantal:

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

Hierdie funksie sal werk in Python 2 en 3 vir al dicts. bv gegee dicts a om g:

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

en sleutel waarde pare in g sal voorrang geniet bo dicts a om f, en so aan.

Kritiek van ander vraag

Moenie wat jy sien in die voorheen aanvaarde antwoord:

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

In Python 2, skep jy twee lyste in die geheue vir elke dict, skep 'n derde lys in die geheue met lengte gelyk aan die lengte van die eerste twee saam te stel, en dan weggooi al drie lyste om die dict skep. In Python 3, dit sal misluk omdat jy die toevoeging van twee dict_items saam voorwerpe, nie twee lyste -

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

en jy sal hê om hulle uitdruklik skep as lyste, bv z = dict(list(x.items()) + list(y.items())). Dit is 'n vermorsing van hulpbronne en berekening krag.

Net so neem die unie van items() in Python 3 (viewitems() in Python 2.7) sal ook misluk wanneer waardes unhashable voorwerpe (soos lyste, byvoorbeeld). Selfs as jou waardes is hashable, sedert stelle is semanties ongeordende, is die gedrag ongedefinieer met betrekking tot voorrang. So moenie dit doen nie:

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

Hierdie voorbeeld demonstrates wat gebeur wanneer waardes unhashable:

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

Hier is 'n voorbeeld waar y voorrang behoort te hê nie, maar eerder die waarde van x is behou as gevolg van die arbitrêre volgorde van stelle:

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

Nog 'n hack moet jy nie gebruik:

z = dict(x, **y)

Dit maak gebruik van die dict konstruktor, en is baie vinnig en geheue doeltreffend (selfs 'n bietjie meer-so as ons twee-stap proses), maar tensy jy weet presies wat hier gebeur (dit is, die tweede dict word geslaag as navraag argumente om die dict constructor), is dit moeilik om te lees, dit is nie die beoogde gebruik, en dus is dit nie Pythonic.

Hier is 'n voorbeeld van die gebruik om remediated in Django .

Dicts is bedoel om hashable sleutels (bv frozensets of tuples), maar neem hierdie metode nie in Python 3 wanneer sleutels is nie snare.

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

Van die poslys , Guido van Rossum , die skepper van die taal, skryf:

  

Ek is goed met   verklaar Dict ({}, ** {1: 3}) onwettige, want na alles is dit is die misbruik van   die ** meganisme.

en

  

Blykbaar Dict (x, ** y) gaan rond as "cool hack" vir "call   x.update (y) en terugkeer x ". Ek persoonlik vind dit meer veragtelike as   cool.

Dit is my verstand (asook die begrip van die skepper van die taal ) wat die beoogde gebruik vir dict(**y) is vir die skep van dicts vir leesbaarheid doeleindes, bv:

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

in plaas van

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

Reaksie op opmerkings

  

Ten spyte van wat Guido sê, dict(x, **y) is in lyn met die dict spesifikasie, wat btw. werk vir beide Python 2 en 3. Die feit dat dit slegs werk vir string sleutels is 'n direkte gevolg van hoe navraag parameters werk en nie 'n kort koms van Dict. Dit is ook nie die gebruik van die ** operateur in hierdie plek 'n misbruik van die meganisme, in werklikheid ** is ontwerp juis om dicts slaag as sleutelwoorde.

Weereens, beteken dit nie werk vir 3 wanneer sleutels is nie-stringe. Die implisiete roeping kontrak is dat naamruimtes neem gewone dicts, terwyl gebruikers net navraag argumente wat snare moet slaag. Alle ander callables afgedwing nie. dict gebreek hierdie konsekwentheid in Python 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}

Dit teenstrydigheid is sleg gegee ander implementering van 'n afgestorwene (Pypy, Jython, IronPython). So dit is vasgestel in Python 3, aangesien dit gebruik 'n breek verandering kan wees.

Ek stuur vir julle dat dit kwaadwillige onbevoegdheid om doelbewus te skryf kode wat net werk in een weergawe van 'n taal of wat net werk gegee sekere arbitrêre beperkings.

Meer kommentaar:

  

dict(x.items() + y.items()) is steeds die mees leesbare oplossing vir Python 2. Leesbaarheid tel.

My reaksie: merge_two_dicts(x, y) eintlik lyk baie duideliker vir my, as ons werklik bekommerd oor leesbaarheid. En dit is nie uit daarna versoenbaar, as Python 2 is toenemend afgekeur.

  

{**x, **y} Dit lyk nie of geneste woordeboeke hanteer. die inhoud van sub-sleutels is eenvoudig oorskryf, nie saamgesmelt [...] Ek het uiteindelik verbrand deur hierdie antwoorde wat nie rekursief doen om saam te smelt en ek was verbaas niemand het dit genoem. In my interpretasie van die woord "samesmelting" hierdie antwoorde beskryf "opdatering een dict met 'n ander", en nie saamsmelt.

Ja. Ek moet jy terugverwys na die vraag, wat vra vir 'n vlak merge van twee woordeboeke, met die fiwaardes eerste se wat oorskryf deur die tweede se -. in 'n enkele uitdrukking

Die aanvaarding van twee woordeboek van woordeboeke, kan 'n mens rekursief saamsmelt hulle in 'n enkele funksie, maar jy moet versigtig wees om die dicts uit óf bron, en die beste manier om te verhoed dat dit is om 'n kopie te maak wanneer die toeken van waardes nie te verander. As sleutels hashable moet wees en is gewoonlik dus onveranderlik, dit is nutteloos om hulle kopieer:

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

Gebruik:

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

Kom met gebeurlikhede vir ander vorme waarde is ver buite die bestek van hierdie vraag, so ek sal jou wys by my antwoord op die kanonieke vraag op 'n "Dictionaries van woordeboeke saam te smelt" .

Minder Performant Maar Korrekte Ad-hocs

Hierdie benaderings is minder performante, maar hulle sal korrekte gedrag te voorsien. Hulle sal wees veel minder performante as copy en update of die nuwe pak, want hulle herhaal deur elke sleutel-waarde paar op 'n hoër vlak van abstraksie, maar hulle nie respekteer die einde van voorrang (laasgenoemde dicts het voorrang)

Jy kan ook met die hand ketting die dicts binne 'n dict begrip:

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

of in python 2.6 (en dalk so vroeg as 2,4 wanneer kragopwekker uitdrukkings is ingestel):

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

itertools.chain wil ketting die iterators oor die sleutel-waarde pare in die korrekte volgorde:

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

Performance Analysis

Ek is net gaan om die prestasie analise van die gebruike bekend om korrek op te tree nie.

import timeit

Die volgende is gedoen op Ubuntu 14,04

In Python 2.7 (stelsel Python):

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

In Python 3.5 (deadsnakes PPA):

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

Resources op Woordeboeke

Ander wenke

In jou geval, wat jy kan doen is:

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

Dit sal as jy dit wil hê, sit die finale dict in z, en maak die waarde vir sleutel b behoorlik oorheers deur die waarde van die tweede (y) dict se:

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

As jy Python 3 te gebruik, dit is net 'n bietjie meer ingewikkeld. Om z skep:

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

'n Alternatiewe:

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

'n ander, meer bondige, opsie:

z = dict(x, **y)

Nota:dit het'n gewilde antwoord, maar dit is belangrik om uit te wys dat as y het enige nie-string sleutels, die feit dat dit werk op alle is'n misbruik van'n CPython implementering detail, en dit nie werk nie in Python 3, of in PyPy, IronPython, of Jython.Ook, Guido is nie'n fan.So ek kan nie beveel hierdie tegniek vir die vorentoe-versoenbaar of kruis-implementering draagbare kode, wat beteken dit regtig moet heeltemal vermy.

Dit sal waarskynlik nie 'n gewilde antwoord wees, maar jy byna seker nie wil hê om dit te doen. As jy 'n afskrif dit is 'n merge wil, gebruik dan kopie (of deepcopy , afhangende op wat jy wil) en dan te werk. Die twee lyne van kode is baie meer leesbare - meer Pythonic - as die enkele lyn skepping met .items () + .items (). Eksplisiete is beter as implisiete.

Verder is daar by jou .items () (pre Python 3.0) gebruik, jy skep 'n nuwe lys wat die items uit die dict bevat. As jou woordeboeke is groot, dan is dit nogal 'n baie van oorhoofse (twee groot lyste wat weg sodra die saamgesmelte dict geskep sal word gegooi). update () kan meer doeltreffend te werk, omdat dit deur die tweede dict item-vir-item kan hardloop.

In terme van tyd :

>>> 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 die klein verlangsaming tussen die eerste twee is die moeite werd vir die leesbaarheid. Daarbenewens is navraag argumente vir woordeboek skepping net bygevoeg in Python 2.3, terwyl kopieer () en werk () sal werk in ouer weergawes.

In 'n opvolg antwoord, vra jy oor die relatiewe prestasie van hierdie twee alternatiewe:

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

Op my masjien, ten minste ( 'n redelik gewone x86_64 hardloop Python 2.5.2), alternatiewe z2 is nie net korter en eenvoudiger, maar ook aansienlik vinniger. Jy kan dit kontroleer vir jouself met behulp van die timeit module wat kom met Python.

Voorbeeld 1: identies woordeboeke kartering 20 opeenvolgende heelgetalle om hulself:

% 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

z2 wen met 'n faktor van 3,5 of so. Verskillende woordeboeke lyk heel anders resultate oplewer, maar z2 lyk altyd voor uit te kom. (As jy strydig resultate vir kry die dieselfde toets, probeer verby in -r met 'n aantal groter as die standaard 3.)

Voorbeeld 2: nie-oorvleuelende woordeboeke kartering 252 kort snare om heelgetalle en omgekeerd:

% 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 wen met sowat 'n faktor van 10. Dit is 'n mooi groot oorwinning in my boek!

Na vergelyk dié twee, ek het gewonder of swak prestasie z1 se kan toegeskryf word aan die oorhoofse van die bou van die twee item lyste, wat op sy beurt het my gelei om te wonder of hierdie variasie beter kan werk:

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

'n Paar vinnige toetse, Bv.

% 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

lei my tot die gevolgtrekking dat z3 is 'n bietjie vinniger as z1, maar nie naastenby so vinnig as wat z2. Beslis nie die moeite werd om al die ekstra tik.

Hierdie draad is steeds vermis iets belangrik, wat is 'n prestasie vergelyking van hierdie alternatiewe met die "ooglopende" manier van samesmelting twee lyste: die gebruik van die update metode. Om te probeer om dinge op 'n gelyke voet met die uitdrukking, waarvan nie een verander x of y hou, ek gaan 'n afskrif van x in plaas maak van die wysiging is dit in-plek, soos volg:

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

'n Tipiese gevolg:

% 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

Met ander woorde, z0 en z2 lyk in wese identies prestasie het. Dink jy dit kan 'n toeval wees? Ek doen nie ....

In werklikheid, sou ek so ver gaan as om te beweer dat dit onmoontlik is vir suiwer Python kode om enige beter as dit doen. En as jy aansienlik beter in 'n C uitbreiding module kan doen, ek dink die Python mense dalk ook belangstel in die integrasie van jou kode (of 'n variasie op jou benadering) in die Python kern. Python gebruik dict in baie plekke; optimalisering van sy bedrywighede is 'n groot deal.

Jy kan ook hierdie as skryf

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

as Tony doen, maar (nie verbasend) die verskil in notasie blyk geen meetbare uitwerking op prestasie te hê. Gebruik wat ook al reg om jou kyk. Natuurlik, hy is absoluut korrek om uit te wys dat die twee-verklaring weergawe is veel makliker om te verstaan.

Ek wou iets soortgelyks, maar met die vermoë om te spesifiseer hoe die waardes op duplikaatsleutels is saamgesmelt, so ek gekap hierdie uit (maar nie swaar toets dit). Dit is duidelik dat hierdie nie 'n enkele uitdrukking, maar dit is 'n enkele funksie oproep.

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

Recursief / diep update n dict

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

Demonstrasie:

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

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

print deepupdate(pluto_original, pluto_update)

Uitsette:

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

Dankie rednaw vir wysigings.

Die beste weergawe waaraan ek kon dink terwyl dit nie met behulp van kopie sal wees:

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

Dit is vinniger as dict(x.items() + y.items()) maar nie so vinnig as wat n = copy(a); n.update(b), ten minste op CPython. Hierdie weergawe werk ook in Python 3 as jy iteritems() verander na items(), wat outomaties word gedoen deur die 2to3 instrument.

Ek persoonlik hou van hierdie weergawe die beste omdat dit redelik goed wat ek wil hê in 'n enkele funksionele sintaksis beskryf. Die enigste klein probleem is dat dit nie heeltemal duidelik dat waardes van y voorrang bo waardes van x maak, maar ek glo nie dit is moeilik om uit te vind dat.

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

Vir items met sleutels in beide woordeboeke ( 'b'), kan jy bepaal watter een beland in die uitset deur 'dat een laaste.

Python 3.5 (PEP 448) kan 'n mooier sintaksis opsie:

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

Of selfs

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

Terwyl die vraag is reeds beantwoord'n paar keer, hierdie eenvoudige oplossing vir die probleem het nie gelys is nie.

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

Dit is so vinnig as z0 en die bose z2 hierbo genoem, maar maklik om te verstaan en te verander.

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

new = dict_merge(old, extras)

Onder sulke skaduwee en twyfelagtige antwoorde, hierdie skitterende voorbeeld is die een en enigste goeie manier om dicts in Python saamsmelt, deur diktator onderskryf lewenslank Guido van Rossum homself! Iemand anders voorgestel helfte van hierdie, maar het nie sit dit in 'n funksie.

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

gee:

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

As jy dink lambdas boos dan lees nie verder nie. Soos versoek, kan jy die vinnige en geheue-doeltreffende oplossing te skryf met een uitdrukking:

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}

Soos hierbo voorgestel, met behulp van twee lyne of die skryf van 'n funksie is waarskynlik 'n beter manier om te gaan.

Wees pythonic. Gebruik 'n begrip :

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

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

In python3, die items metode nie meer gee 'n lys , maar eerder 'n view , wat optree soos 'n stel. In hierdie geval wat jy nodig het om die stel unie neem sedert concatenating met + sal nie werk nie:

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

Vir python3-agtige gedrag in weergawe 2.7, behoort die viewitems metode werk in plaas van items:

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

Ek verkies hierdie notasie anyways, aangesien dit lyk meer natuurlike om te dink aan dit as 'n stel unie werking eerder as aaneenskakellling (soos die titel shows).

Edit:

'n Paar meer punte vir luislang 3. In die eerste plek daarop dat die dict(x, **y) truuk sal nie werk in python 3 tensy die sleutels in y is snare.

Ook, Raymond Hettinger se Chainmap antwoord is mooi elegante, aangesien dit 'n arbitrêre aantal dicts as argumente kan neem, maar van die dokumente dit lyk asof dit in volgorde kyk deur 'n lys van al die dicts vir elke lookup :

  

soektogte soek die onderliggende afbeeldings agtereenvolgens tot 'n sleutel is gevind.

Dit kan stadig jy neer as jy het 'n baie soektogte in jou aansoek:

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

So oor 'n orde van grootte stadiger vir soektogte. Ek is 'n fan van Chainmap, maar lyk minder praktiese waar daar dalk 'n baie soektogte.

misbruik lei tot 'n een-uitdrukking oplossing vir Matthew se antwoord :

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

Jy het gesê jy een uitdrukking wou, so ek mishandel lambda om 'n naam te bind, en tuples om lambda se een-uitdrukking limiet ignoreer. Voel vry om ineenkrimp.

Jy kan ook dit te doen natuurlik as jy nie omgee kopiëring dit:

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

Eenvoudige oplossing met behulp van itertools daardie volgorde (laasgenoemde dicts het voorrang)

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

En dis gebruik:

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

Twee woordeboeke

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

n woordeboeke

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

sum het slegte prestasie. Sien https://mathieularose.com/how -nie-tot-plat-'n-lys-van-lyste-in-luislang /

Hoewel die antwoorde was goed vir hierdie vlak woordeboek, nie een van die metodes hier gedefinieer eintlik doen 'n diep woordeboek merge.

Voorbeelde volg:

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

'n Mens sou verwag dat 'n gevolg van iets soos hierdie:

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

In plaas daarvan, dit kry ons:

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

Die 'een' inskrywing moet 'depth_2 "en" ekstra "as items in sy woordeboek gehad het as dit werklik was 'n merge.

Die gebruik van ketting ook, nie werk nie:

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

Resultate in:

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

Die diep merge dat rcwesick het ook skep dieselfde resultaat.

Ja, sal dit werk om die monster woordeboeke saam te smelt, maar nie een van hulle is 'n generiese meganisme om saam te smelt. Ek sal dit later op te dateer wanneer ek skryf 'n metode wat 'n ware saamsmelt nie.

Drawing op idees hier en elders het ek 'n funksie begryp:

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

Gebruik (getoets in python 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})

Jy kan 'n lambda gebruik in plaas.

Die probleem wat ek het met gelys to date oplossings is dat, in die saamgesmelte woordeboek, die waarde vir sleutel "b" is 10 maar, na my manier van dink, moet dit wees 12. In daardie lig, bied ek die volgende:

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

Resultate:

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

Dit kan gedoen word met 'n enkele dict begrip:

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

Na my mening die beste antwoord vir die 'n enkele uitdrukking 'n deel as geen ekstra funksies wat nodig is, en dit is kort.

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

Dit behoort jou probleem op te los.

(Vir Python2.7 * net;. Daar is eenvoudiger oplossings vir Python3 *)

As jy nie sku om die invoer van 'n standaard biblioteek module, kan jy dit doen

from functools import reduce

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

(Die or a bietjie in die lambda is nodig omdat dict.update altyd terug None op sukses.)

Dit is so dom dat .update terugkeer niks.
Ek gebruik net 'n eenvoudige helper funksie om die probleem op te los:

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

Voorbeelde:

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

Die gebruik van 'n dict begrip, kan jy

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

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}

gee

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

Let op die sintaksis vir if else in begrip

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }

Ek weet dit maak nie regtig pas by die besonderhede van die vrae ( "een sak"), maar sedert geen van die antwoorde hierbo het in hierdie rigting, terwyl baie, baie antwoorde die prestasie kwessie aangespreek Ek het gevoel ek moet my gedagtes dra.

Na gelang van die gebruik geval is dit dalk nie nodig om 'n "regte" saamgesmelte woordeboek van die gegewe insette woordeboeke te skep. A view wat beteken dit kan genoeg wees in baie gevalle, i. e. 'n voorwerp wat optree soos die saamgesmelte woordeboek sou daarsonder heeltemal rekenaar. A lui weergawe van die saamgesmelte woordeboek, om so te praat.

In Python, dit is redelik eenvoudig en kan gedoen word met die vertoon aan die einde van my post kode. Dit gegee, die antwoord op die oorspronklike vraag sou wees:

z = MergeDict(x, y)

By die gebruik van hierdie nuwe voorwerp, sal dit gedra soos 'n saamgesmelte woordeboek maar dit sal n konstante skepping tyd en konstante geheue voetspoor hê terwyl die verlaat van die oorspronklike woordeboeke onaangeraak. Skep dit weg goedkoper as in die ander oplossings voorgestel.

Natuurlik, as jy die resultaat 'n baie gebruik, dan sal jy op 'n stadium bereik die limiet waar die skep van 'n ware saamgesmelt woordeboek sou gewees het die vinniger oplossing. Soos ek gesê het, dit hang af van jou gebruik geval.

As jy al ooit gevoel jy sou verkies om 'n ware saamgesmelt dict het, dan roep dict(z) sou dit produseer (maar weg duurder as die ander oplossings natuurlik, so dit is net noemenswaardig).

Jy kan ook hierdie klas gebruik om 'n soort van kopie-op-skryf woordeboek maak:

a = { 'x': 3, 'y': 4 }
b = MergeDict(a)  # we merge just one dict
b['x'] = 5
print b  # will print {'x': 5, 'y': 4}
print a  # will print {'y': 4, 'x': 3}

Hier is die reguit-vorentoe-kode van MergeDict:

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())
Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top