Python で辞書をコピーする

Rayven Esplanada 2023年1月30日
  1. Python で辞書をコピーする:参照による受け渡し
  2. Python で辞書をコピーする:値で渡す
  3. Python 辞書の浅いコピー
Python で辞書をコピーする

このチュートリアルでは、Python で辞書をコピーする方法について説明します。

今回は、値で渡す方法と参照で渡す方法の 2つの方法で辞書をコピーする方法を実演します。

Python で辞書をコピーする:参照による受け渡し

Python では、オブジェクトは暗黙のうちにコピーされません。もし food を新しい変数 meal にコピーしようとすると、food の値は meal にコピーされますが、food の参照もコピーされます。

meal = food

あるオブジェクトを別のオブジェクトに直接等しくすると、新しいオブジェクトは前のオブジェクトを指すようになります。これは 2つの変数が同じ一意のオブジェクトを参照することを意味します。

mealFruit の値を Banana に更新すると、foodFruit の値も置換されます。

meal["Fruit"] = "Banana"
print(food)
print(meal)

出力:

{'Fruit': 'Banana', 'Vegetable': 'Lettuce', 'Poultry': 'Chicken', 'Fish': 'Cod'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce', 'Poultry': 'Chicken', 'Fish': 'Cod'}

meal ブロックのキーを更新しても同様です。キー FruitCircle 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'}

これは、foodmeal にコピーする際に使用したメソッドが参照渡しになっているためです。

Python のコピー辞書 - 参照渡し

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)

新しいオブジェクト mealdict()copy() を使ってインスタンス化することで、元のオブジェクトを参照して meal が更新された場合にその値を更新しなくて済むようになります。

Python 辞書の浅いコピー

dict()copy() の問題点は、使用するオブジェクトに対して浅いコピーしか適用されないことです。

浅いコピーは、ネストされたオブジェクトが新しいスペースを占有するため、それが見たメモリ内の最初のレイヤーだけをコピーします。

Python のコピー辞書 - 浅いコピー

元のオブジェクトを入れ子になった辞書に変更してみましょう。

info = {
    "Numbers": [1, 2, 3],
    "Resident Name": "Sherlock",
    "Address": {"Street": "Baker", "Number": "221B", "City": "Miami"},
}

新しいオブジェクト info2copy()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'}}

コピー元とコピー元の両方で NumbersAddress.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() 関数を使用することです。

Rayven Esplanada avatar Rayven Esplanada avatar

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

関連記事 - Python Dictionary