[Python] 如何在單個表達式中合併兩個字典?


Answers

在你的情況下,你可以做的是:

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

這將根據你的需要,把最後一個字典放在z ,並且使第二個字母( y )的值正確地覆蓋鍵b的值:

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

如果你使用Python 3,它只會更複雜一點。 要創建z

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

我有兩個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}

我怎樣才能得到最終的合併字典在z ,而不是x

(為了更加清楚, dict.update()的最後一次沖突處理也是我正在尋找的。)




在Python 3.5中,您可以使用unpack **來創建新的字典。 這種方法在過去的答案中沒有顯示出來。 另外,最好使用{}而不是dict() 。 因為{}是一個python文字,而dict()涉及一個函數調用。

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






至今我列出的解決方案中存在的問題是,在合併字典中,鍵“b”的值為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}



我可以在不使用複制的情況下思考最好的版本:

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

它比dict(x.items() + y.items())快,但不如n = copy(a); n.update(b)n = copy(a); n.update(b) n = copy(a); n.update(b) ,至少在CPython上。 如果您將iteritems()更改為items() ,該版本也可以在Python 3中運行,這是由2to3工具自動完成的。

就我個人而言,我最喜歡這個版本,因為它在單個函數語法中描述的相當不錯。 唯一的小問題是,它並不完全顯而易見,來自y的值優先於來自x的值,但我不認為很難弄清楚。




在python3中, items方法不再返回一個列表 ,而是一個視圖 ,它就像一個集合。 在這種情況下,您需要使用set union,因為與+連接將不起作用:

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

對於版本2.7中的類似python3的行為, viewitems方法應該能夠代替items

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

無論如何,我更喜歡這種表示法,因為把它看作是一種聯合操作而不是串聯(如標題所示)似乎更自然。

編輯:

首先,請注意,除非y中的鍵是字符串,否則dict(x, **y)技巧在python 3中將不起作用。

此外,Raymond Hettinger的Chainmap answer非常優雅,因為它可以將任意數量的字符串作為參數,但從文檔看來,它依次查看每個查找的所有字典列表:

查找先後搜索底層映射,直到找到密鑰。

如果您的應用程序中有很多查找,這會降低速度:

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

所以查找速度要慢一個數量級。 我是Chainmap的粉絲,但在可能有很多查找的地方看起來不太實用。




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






另一個更簡潔的選項:

z = dict(x, **y)

注意 :這已經成為一個受歡迎的答案,但是重要的是要指出,如果y有任何非字符串鍵,則這個工作原理完全是濫用CPython實現細節,並且在Python 3中不起作用,或者在PyPy,IronPython或Jython中。 另外, Guido不是粉絲 。 所以我不推薦這種技術用於向前兼容或跨實現的可移植代碼,這意味著它應該完全避免。




虐待導致馬修的答案為單表達式解決方案:

>>> 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來綁定一個名稱,並且元組覆蓋lambda的單表達式限制。 隨時感到畏縮。

當然,如果你不關心複製它,你也可以這樣做:

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



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

對於在這兩個詞典中都有鍵的項目('b'),可以通過最後一個輸入來控制輸出中哪一個結束。




Using a dict comprehension, you may

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

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

Note the syntax for if else in comprehension

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



在Python 3中,可以使用collections.ChainMap將多個字典或其他映射組合在一起以創建單個可更新視圖:

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



在後續答案中,您詢問了這兩個備選方案的相對錶現:

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

在我的機器上,至少(一個運行Python 2.5.2的相當普通的x86_64),替代z2不僅更短,更簡單,而且速度更快。 您可以使用Python附帶的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

z2以3.5左右的係數獲勝。 不同的字典似乎產生了截然不同的結果,但z2似乎總是出現。 (如果您對同一測試的結果不一致,請嘗試使用大於默認值3的數字傳入-r 。)

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

導致我得出結論: z3z1快一些,但幾乎不如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

換句話說, z0z2似乎具有基本相同的性能。 你認為這可能是巧合嗎? 我不....

事實上,我甚至會聲稱純Python代碼不可能做得比這更好。 如果你可以在C擴展模塊中做得更好,我想Python的人可能會將你的代碼(或者你的方法的變體)整合到Python核心中。 Python在很多地方使用dict ; 優化其運營是一件大事。

你也可以這樣寫

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

正如托尼所做的那樣,但是(並不奇怪)表示法的區別不會對性能產生任何可衡量的影響。 使用任何看起來正確的東西給你。 當然,他非常正確地指出,雙語版本更容易理解。




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

new = dict_merge(old, extras)

在這樣可笑和可疑的答案中,這個光輝的例子是合併Python中的字典的獨一無二的好方法,由獨裁者Guido van Rossum親自批准! 其他人提出了這個的一半,但沒有把它放在一個函數中。

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

得到:

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