Python で辞書をコピーする
このチュートリアルでは、Python で辞書をコピーする方法について説明します。
今回は、値で渡す方法と参照で渡す方法の 2つの方法で辞書をコピーする方法を実演します。
Python で辞書をコピーする:参照による受け渡し
Python では、オブジェクトは暗黙のうちにコピーされません。もし food
を新しい変数 meal
にコピーしようとすると、food
の値は meal
にコピーされますが、food
の参照もコピーされます。
meal = food
あるオブジェクトを別のオブジェクトに直接等しくすると、新しいオブジェクトは前のオブジェクトを指すようになります。これは 2つの変数が同じ一意のオブジェクトを参照することを意味します。
meal
の Fruit
の値を Banana
に更新すると、food
の Fruit
の値も置換されます。
meal["Fruit"] = "Banana"
print(food)
print(meal)
出力:
{'Fruit': 'Banana', 'Vegetable': 'Lettuce', 'Poultry': 'Chicken', 'Fish': 'Cod'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce', 'Poultry': 'Chicken', 'Fish': 'Cod'}
meal
ブロックのキーを更新しても同様です。キー Fruit
を Circle Fruit
に置き換え、その値をコピーしてから辞書から取り出します。
meal["Circle Fruit"] = meal.pop("Fruit")
print(food)
print(meal)
出力:
{'Vegetable': 'Lettuce', 'Poultry': 'Chicken', 'Fish': 'Cod', 'Circle Fruit': 'Orange'}
{'Vegetable': 'Lettuce', 'Poultry': 'Chicken', 'Fish': 'Cod', 'Circle Fruit': 'Orange'}
これは、food
を meal
にコピーする際に使用したメソッドが参照渡しになっているためです。
Python で辞書をコピーする:値で渡す
値で渡すということは、オブジェクトをコピーする際にコピーを元のオブジェクトに指すのではなく、実際のコピーがメモリ内に作成されることを意味します。
辞書をコピーして元の値を参照しないようにしたいのであれば、メモリ上に新しいオブジェクトをインスタンス化する方法を見つけなければなりません。Python には、この方法をサポートする関数がいくつかあります: dict()
、copy()
、deepcopy()
です。
関数 dict()
は新しい辞書オブジェクトのインスタンスを作成します。既存の辞書をこの関数に巻き付けると、オブジェクトの新しいインスタンスが生成されます。
このメソッドでは、辞書 food
の同じ例を使用することにします。
meal = dict(food)
値を渡すもう 1つの方法は、copy()
コマンドを使用することです。このコマンドは、dict()
と同じことを実行します。つまり、メモリ内の新しいオブジェクトをインスタンス化します。違いは、copy()
が辞書を含むコレクションオブジェクトの組み込み関数であるということです。
meal = food.copy()
両方のシナリオについて、Fruit
の値を変更して、Vegetable
のキーを置き換えてみましょう。
meal["Fruit"] = "Apple"
meal["Greens"] = meal.pop("Vegetable")
print(food)
print(meal)
出力:
{'Fruit': 'Orange', 'Vegetable': 'Lettuce', 'Poultry': 'Chicken', 'Fish': 'Cod'} # food (original)
{'Fruit': 'Apple', 'Poultry': 'Chicken', 'Fish': 'Cod', 'Greens': 'Lettuce'} # meal (copy)
新しいオブジェクト meal
を dict()
や copy()
を使ってインスタンス化することで、元のオブジェクトを参照して meal
が更新された場合にその値を更新しなくて済むようになります。
Python 辞書の浅いコピー
dict()
と copy()
の問題点は、使用するオブジェクトに対して浅いコピーしか適用されないことです。
浅いコピーは、ネストされたオブジェクトが新しいスペースを占有するため、それが見たメモリ内の最初のレイヤーだけをコピーします。
元のオブジェクトを入れ子になった辞書に変更してみましょう。
info = {
"Numbers": [1, 2, 3],
"Resident Name": "Sherlock",
"Address": {"Street": "Baker", "Number": "221B", "City": "Miami"},
}
新しいオブジェクト info2
を copy()
と dict()
を用いて宣言し、info
からコピーしてネストされた辞書の値を変更してみる。
info2 = info.copy() # or dict(info)
info2["Numbers"][1] = 4
info2["Resident Name"] = "Holmes"
info2["Address"]["City"] = "Lexington"
print(info)
print(info2)
出力:
{'Numbers': [1, 4, 3], 'Resident Name': 'Sherlock', 'Address': {'Street': 'Baker', 'Number': '221B', 'City': 'Lexington'}}
{'Numbers': [1, 4, 3], 'Resident Name': 'Holmes', 'Address': {'Street': 'Baker', 'Number': '221B', 'City': 'Lexington'}}
コピー元とコピー元の両方で Numbers
と Address.City
の値が更新されます。Resident Name
の値は info2
ブロックを更新しただけで、浅いコピーを行っただけなので、info2
ブロックは更新されませんでした。
Python の copy
モジュールを使ったディープコピー
ディープコピーは、本質的に浅いコピーの問題を修正します。オブジェクトをコピーすると、入れ子になったオブジェクトをチェックし、メモリ内に新しいオブジェクトを再帰的に生成します。
Python では、浅いコピーと深いコピーの操作とユーティリティを含むモジュール copy
を使って深いコピーを実現することができます。
import copy
モジュールの deepcopy()
関数を使って、辞書内のネストしたオブジェクトをディープコピーします。上記の info
ブロックと同じ例を使用します。
info2 = copy.deepcopy(info)
info2["Numbers"][1] = 4
info2["Resident Name"] = "Holmes"
info2["Address"]["City"] = "Lexington"
print(info)
print(info2)
出力:
{'Numbers': [1, 2, 3], 'Resident Name': 'Sherlock', 'Address': {'Street': 'Baker', 'Number': '221B', 'City': 'Miami'}}
{'Numbers': [1, 4, 3], 'Resident Name': 'Holmes', 'Address': {'Street': 'Baker', 'Number': '221B', 'City': 'Lexington'}}
これで、元の辞書 info
は、入れ子になったオブジェクトを含めて info2
に複数の変更があっても、変更されていません。
まとめると、Python で辞書をコピーする方法はたくさんありますが、出力はすべて同じではありません。辞書を =
で直接代入すると、元のオブジェクトを指す参照で辞書を渡すことになります。
dict()
や copy()
のような浅いコピー関数は、入れ子になっていない辞書に対してのみこの問題を解決します。
入れ子になった辞書を考慮して辞書をコピーする最良の方法は、copy
モジュールが提供する deepcopy()
関数を使用することです。
Skilled in Python, Java, Spring Boot, AngularJS, and Agile Methodologies. Strong engineering professional with a passion for development and always seeking opportunities for personal and career growth. A Technical Writer writing about comprehensive how-to articles, environment set-ups, and technical walkthroughs. Specializes in writing Python, Java, Spring, and SQL articles.
LinkedIn