要件
- 辞書
d1
とd2
をマージして新しい辞書を作りたい. - 重複するキーがあった場合は
d2
の値を優先することにする.- たとえば
{'a': 1, 'b': 2}
と{'b': 3, 'c': 4}
をマージすると{'a': 1, 'b': 3, 'c': 4}
となってほしい.
- たとえば
- マージをする過程で
d1
やd2
の中身は書き換えたくない.
方法
dict(d1, **d2)
d2
をキーワード引数として展開して dict
のコンストラクタに渡すという方法.
キーワード引数が与えられた場合、キーワード引数とその値が位置引数から作られた辞書に追加されます。既に存在しているキーが追加された場合、キーワード引数の値は位置引数の値を置き換えます。
確かにそれらしいが謎ハックぽさが否めないので,いきなり披露されるとややびっくりしそう.
dict(d1.items() + d2.items())
dict.items()
が返す (k, v)
のリストを結合したリストをもとに dict を作るという方法.
マージしているんだろうなあというのは式から見える気がするが,いちいちリストに変換するのでコストが大きい.
Python3系では
dict.items()
がビューオブジェクトを返すようになったため,この方法は2系のみでしか使えない.itertools.chain
を使うことでイテレータに対しても同等のことができる(追記参照).
dict(itertools.chain(d1.items(), d2.items())
newdict = dict(d1); newdict.update(d2)
一時変数 newdict
に d1
のコピーを作って,そこに d2
をマージするという方法.
やっていることは一番素直なのではないだろうか.
ただし一時変数が必要となる.
{**d1, **d2}
(追記参照)
辞書リテラル内で辞書展開記法を使うという方法. 文字数が一番短かくてすっきりした印象があってよさそう. ただしPython3.5以上でしか使えない.
d1 | d2
(追記参照)
Python 3.9 にはついに辞書をマージして返す二項演算子が追加されたので、この記事は無用になりそう。もともとsetについてはそのような演算子があったので、その延長だと考えると自然?
>>> {'a': 1, 'b': 2} | {'b': 3, 'c': 4} {'a': 1, 'b': 3, 'c': 4}
まとめ
newdict = dict(d1)
newdict.update(d2)
が一番素直かなあという感想.
辞書リテラルに対して直接マージしたい!! というときは,
newdict = dict({'a': 1, 'b': 2}, **d2)
とやるのがまあいいかなあと思っている. Python3.5以上なら,
{**d1, **d2}
のほうがスマートな感じがする(追記参照).
Python 3.9 以上なら d1 | d2
でよい。
追記
Python 3 なら dict(itertools.chain(d1.items(), d2.items())),Python 3.5+ なら {**d1, **d2} でも良さそう https://t.co/idccLS9mXM
— ymyzk (@ymyzk) 2017年8月3日
items 結合できないと思っていましたが itertools.chain
でイテレータを連続させられるので確かに同等なことができますね.
辞書リテラルの中でもキーワード引数展開記法が使えるのは知らなかった…….この記法なら辞書リテラルに対しても直接マージできそう.
追記
Python 3.9 には、辞書をマージする二項演算子 |
が追加されました。