クラスで学んだ浅いコピーと深いコピーの違いを理解しています。ただし、以下は意味がありません
_import copy
a = [1, 2, 3, 4, 5]
b = copy.deepcopy(a)
print(a is b)
print(a[0] is b[0])
----------------------------
~Output~
>False
>True
----------------------------
_
オブジェクトとその構成要素がディープコピーの別のメモリロケーションに再作成されているため、print(a[0] is b[0])
はFalseに評価されませんか?クラスでこれを話し合ったので、私はこれをテストしてみましたが、うまくいかないようです。
これは、Pythonが小さな整数のオブジェクトをインターン化しているためと思われます。このステートメントは正しいですが、その動作の原因ではありません。
大きな整数を使用するとどうなるかを見てみましょう。
> from copy import deepcopy
> x = 1000
> x is deepcopy(x)
True
copy
モジュールを掘り下げると、アトミック値でdeepcopy
を呼び出すと、関数の呼び出しが遅延することがわかります _deepcopy_atomic
。
def _deepcopy_atomic(x, memo):
return x
つまり、実際に起こっていることは、deepcopy
がアトミック値をコピーせず、それだけを返すということです。
例として、これはint
、float
、str
、function
などの場合です。
この動作の理由は、Pythonが最適化されて小さな整数が実際に別のメモリロケーションにないようにするためです。id
of 1
をチェックしてください。これらは常に同じ:
>>> x = 1
>>> y = 1
>>> id(x)
1353557072
>>> id(y)
1353557072
>>> a = [1, 2, 3, 4, 5]
>>> id(a[0])
1353557072
>>> import copy
>>> b = copy.deepcopy(a)
>>> id(b[0])
1353557072
整数オブジェクト からの参照:
現在の実装では、
-5
と256
の間のすべての整数の整数オブジェクトの配列を保持しています。その範囲でintを作成すると、実際には既存のオブジェクトへの参照が返されます。したがって、1
の値を変更できるはずです。 Pythonは未定義です。:-)
OlivierMelançonの答えは、deepcopy
関数呼び出しが、同じint
オブジェクトへの参照を、それらのコピーではなく、どのようにして返すかという機械的な質問だとしたら、正しい答えです。私は一歩下がって、なぜdeepcopy
が賢明なことなのかという質問に答えます。
データ構造のコピー(深いコピーまたは浅いコピー)を作成する必要があるのは、元の状態に影響を与えずに内容を変更できるようにするためです。または、古い状態のコピーを保持したままoriginalを変更できます。データ構造にそれ自体が変更可能なネストされた部分がある場合、その目的のためにディープコピーが必要です。 [[1, 2], [3, 4]]
のように、2Dグリッドのすべての数値を乗算する次の例を考えてみます。
import copy
def multiply_grid(grid, k):
new_grid = copy.deepcopy(grid)
for row in new_grid:
for i in range(len(row)):
row[i] *= k
return new_grid
リストなどのオブジェクトは変更可能であるため、操作row[i] *= k
は状態を変更します。リストのコピーを作成することは、変異を防ぐ方法です。外部リストと内部リスト(つまり行)の両方のコピーを作成するには、ここでもディープコピーが必要です。これらも変更可能です。
ただし、整数や文字列などのオブジェクトは不変であるため、状態を変更することはできません。 int
オブジェクトが13の場合、k
を掛けても13のままです。乗算の結果、別のint
オブジェクトが生成されます。防御すべき変異はないため、コピーを作成する必要はありません。
興味深いことに、deepcopy
は、コンポーネントがすべて不変である場合はタプルのコピーを作成しませんが、変更可能なコンポーネントがある場合はタプルのコピーを作成します。
>>> import copy
>>> x = ([1, 2], [3, 4])
>>> x is copy.deepcopy(x)
False
>>> y = (1, 2)
>>> y is copy.deepcopy(y)
True
ロジックは同じです。オブジェクトが不変であるが、変更可能なネストされたコンポーネントがある場合、元のコンポーネントへの変更を避けるためにコピーが必要です。しかし、構造全体が完全に不変である場合、防御するための変異はなく、したがってコピーは必要ありません。