HOWTO · Python
Python で辞書をコピーする
代入すると辞書の別名が作られ、浅いコピーでは外側の辞書だけが独立します。ネストした値も独立させるには、ディープコピーを使います。
このページの内容
新しい外側の辞書を作るには、dict(source) または source.copy() を使います。どちらも浅いコピーなので、ネストした値は共有されます。ネストした可変値も独立させるには、copy.deepcopy(source) を使います。= による代入ではコピーは作られません。詳しくは、Python の辞書に関するチュートリアルと、Python のcopy モジュールのドキュメントを参照してください。
辞書への代入は別名を作る
代入すると、同じ辞書オブジェクトを指す別の名前が作られます。どちらの名前から辞書を変更しても、その変更は両方から見えます。一方、名前の再代入は別の操作です。その名前の参照先だけが変わり、元のオブジェクトやもう一方の名前には影響しません。
次の例では、meal を通じて共有辞書を変更した後、meal を新しい辞書に再代入します。
food = {"Fruit": "Orange", "Vegetable": "Lettuce"}
meal = food
meal["Fruit"] = "Banana"
print(food)
print(meal)
meal = {"Fruit": "Apple"}
print(food)
print(meal)
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Apple'}
最初の2つの出力値が一致するのは、その時点では両方の名前が同じオブジェクトを指しているためです。meal に別の辞書を代入すると、新しく作成した辞書を指すのは meal だけになり、food は元の辞書を指したままです。この違いを理解すると、一方の名前から値を変更したときにもう一方にも反映される理由と、変数を別の値に置き換えても、もう一方が参照するオブジェクトは置き換わらない理由が分かります。
辞書の浅いコピーを作成する
組み込みの dict() コンストラクターと、辞書の copy() メソッドは、どちらも新しい外側の辞書を作成します。そのコピーのトップレベルの項目を更新または削除しても、元の辞書は変わりません。ただし、2つの辞書は同じネストしたリスト、辞書、その他の可変値を参照します。そのため、そうした値への変更は両方の名前から見えることがあります。
この例では、by_constructor と by_method にそれぞれ独立した外側の辞書が作られます。ネストしたリストは共有されますが、name キーに新しい値を代入すると、選んだコピーだけが変わります。
source = {"name": "Sherlock", "numbers": [1, 2, 3]}
by_constructor = dict(source)
by_method = source.copy()
by_constructor["name"] = "Holmes"
by_constructor["numbers"][1] = 4
print(source)
print(by_constructor)
print(by_method)
{'name': 'Sherlock', 'numbers': [1, 4, 3]}
{'name': 'Holmes', 'numbers': [1, 4, 3]}
{'name': 'Sherlock', 'numbers': [1, 4, 3]}
外側の辞書を独立させ、ネストした値の共有に問題がない場合は、どちらの浅いコピー方法を使っても構いません。浅いコピーでは、ネストしたオブジェクトは再帰的にコピーされません。たとえば、by_constructor["name"] に別の値を代入しても、name キーは別々の外側の辞書に属するため、元の辞書の name は変わりません。一方、numbers の要素を変更すると、リスト自体が共有されているため、出力される3つの辞書すべてから変更が見えます。値が別の辞書の場合も同じで、トップレベルのコピーは内側の辞書への参照を保持します。ネストした値を1つだけ独立させたい場合は、その値を個別にコピーできます。ネストした構造全体を独立させる必要がある場合は、ディープコピーを使います。
ネストした辞書をディープコピーする
ネストした可変値も独立したコピーにする必要がある場合は、copy.deepcopy() を使います。含まれるオブジェクトを再帰的にコピーするため、次の例ではリストの要素や内側の辞書の値を変更しても、元の辞書は変わりません。独立して編集するネストしたデータには便利ですが、ネストした値を共有する意図がある場合には必要ありません。
import copy
source = {
"numbers": [1, 2, 3],
"address": {"city": "Miami"},
}
independent = copy.deepcopy(source)
independent["numbers"][1] = 4
independent["address"]["city"] = "Lexington"
print(source)
print(independent)
{'numbers': [1, 2, 3], 'address': {'city': 'Miami'}}
{'numbers': [1, 4, 3], 'address': {'city': 'Lexington'}}
ディープコピーは、ネストした設定の作業用コピーを作って個別に変更する場合のように、元の構造への変更が作業用コピーに、またはその逆に影響しないようにしたいときに便利です。外側の辞書だけでなくネストしたコンテナーもコピーするため、上の例ではリストと内側の address 辞書の両方が変更されずに残ります。大きな構造では、ディープコピーは浅いコピーより時間とメモリーを多く使うことがあるため、ネストしたオブジェクトを独立させる必要があるかどうかに応じて選んでください。Python のオブジェクトはコピー時の動作をカスタマイズでき、複製しても意味のないリソースもあります。そのようなオブジェクトを扱う場合は、copy モジュールのドキュメントを参照してください。
どのオブジェクトを共有するかが、実際の選択基準です。代入では辞書全体を共有し、浅いコピーでは外側の辞書だけを分け、ディープコピーではネストしたオブジェクトも分けます。copy モジュールのドキュメントでは、複合オブジェクトやコピー動作をカスタマイズしたオブジェクトの浅いコピーとディープコピーについて説明しています。