これは日々の作業を通して学んだことや毎日の生活で気づいたことをを記録しておく備忘録である。
HTML ファイル生成日時: 2024/11/21 17:40:55.112 (台灣標準時)
Python 関連の文書を読んでいると、 shallow copy と deep copy という言葉 が出てきたでござる。よくわからなかったので、調べてみたでござる。
まず、基本は、以下のことのようでござる。
Assignment statements in Python do not copy objects, they create bindings between a target and an object.つまり、
b = aという代入によって何が起きるかと言うと、専門 家ではないので正確な表現でなさそうでござるが、 a の値が複製されて b に 与えられる、ということではなく、 a が指し示す実体の位置が b に与えられ るというような意味のようでござる。
どのようなときに shallow copy と deep copy が必要になるかというと、以 下のような場合だそうでござる。
For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other.mutable なオブジェクトを含む collections をコピーするときだそうでござる。
shallow copy と deep copy は以下のように説明されているでござる。まず、 shallow copy は以下の通りでござる。
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.次に、 deep copy は以下の通りでござる。
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.また、 compound object とは、 "objects that contain other objects, like lists or class instances" だそうでござる。
shallow copy だと、新たなオブジェクトが作られ、そこにもともとの compound object のなかの要素の reference が、新たに作られたオブジェク トに入れられるようでござる。一方で、 deep copy だと、新たなオブジェク トが作られ、そこにもともとの compound object のなかの要素のコピーが、 新たに作られたオブジェクトに入れられるようでござる。そのときに、もとも との compound object のなかの要素は、再帰的にコピーされるようでござる。 つまり、例えば、リストのなかにリストがあったら、リストのなかのリストも、 それぞれの値が新たなオブジェクトにコピーされるようでござる。よって、一 次元のリストや辞書の場合、 shallow copy と deep copy は同じことをする ようでござる。多次元のリストや辞書の場合、 shallow copy と deep copy の結果は違ってくるようでござる。
スカラーについて、代入を行ってみるでござる。以下のコードを用意したでご ざる。
実行結果は以下の通りでござる。#!/usr/pkg/bin/python3.9 # # Time-stamp: <2022/08/20 15:45:40 (CST) daisuke> # print ("executing 'a = 2'") a = 2 print (" value of a =", a) print (" identity of a =", id (a) ) print ("executing 'b = a'") b = a print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) ) print ("executing 'a = 3'") a = 3 print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) )
% ./test_copy_00.py executing 'a = 2' value of a = 2 identity of a = 128063115999872 executing 'b = a' value of a = 2 identity of a = 128063115999872 value of b = 2 identity of b = 128063115999872 executing 'a = 3' value of a = 3 identity of a = 128063115999904 value of b = 2 identity of b = 128063115999872
a = 2を実行すると、 a というオブジェクトが生成されるでござ る。そのあとで、
b = aを実行すると、 b というオブジェクト が生成されるでござる。この b というオブジェクトは、 a と同じ identity を持っているでござる。つまり、 a と b は別の名前を持っているわけでござ るが、実体は同じでござる。更に、このあとで、
a = 3として、 a の値を変更してみると、その時点で、 a と b は異なるオブジェクトになる ようでござる。つまり、 a と b が同じオブジェクトであると困る状況になっ たときに、 a と b が異なるオブジェクトになるようでござる。
次に、一次元のリストについて、代入を行ってみるでござる。以下のコードを 用意したでござる。
実行結果は以下の通りでござる。#!/usr/pkg/bin/python3.9 # # Time-stamp: <2022/08/20 15:46:16 (CST) daisuke> # print ("executing 'a = [ 10, 20, 30 ]'") a = [ 10, 20, 30 ] print (" value of a =", a) print (" identity of a =", id (a) ) print ("executing 'b = a'") b = a print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) ) print ("executing 'a[1] = 100'") a[1] = 100 print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) )
% ./test_copy_01.py executing 'a = [ 10, 20, 30 ]' value of a = [10, 20, 30] identity of a = 134615029306448 executing 'b = a' value of a = [10, 20, 30] identity of a = 134615029306448 value of b = [10, 20, 30] identity of b = 134615029306448 executing 'a[1] = 100' value of a = [10, 100, 30] identity of a = 134615029306448 value of b = [10, 100, 30] identity of b = 134615029306448
b = aを実行すると、 b が生成され、 a と b の実体は同じでご ざる。その後で、
a[1] = 100を実行して、 a の要素の一つを変 更してみるでござる。すると、 a と b は同じものを指し示しているので、 a が変更されると、 b の値も変更されるでござる。もともとのリストの内容を そのまま残しておきたくて、
b = aを実行したのであれば、期待 とは違う結果になっているでござる。
多次元のリストに対して、同様のことをしてみるでござる。以下のコードを用 意したでござる。
実行結果は以下の通りでござる。#!/usr/pkg/bin/python3.9 # # Time-stamp: <2022/08/20 15:46:37 (CST) daisuke> # print ("executing 'a = [ 10, 20, 30 ]'") a = [ 10, 20, 30, [500, 600, 700] ] print (" value of a =", a) print (" identity of a =", id (a) ) print ("executing 'b = a'") b = a print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) ) print ("executing 'a[3][1] = 4000'") a[3][1] = 4000 print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) )
./test_copy_02.py executing 'a = [ 10, 20, 30 ]' value of a = [10, 20, 30, [500, 600, 700]] identity of a = 126803974160848 executing 'b = a' value of a = [10, 20, 30, [500, 600, 700]] identity of a = 126803974160848 value of b = [10, 20, 30, [500, 600, 700]] identity of b = 126803974160848 executing 'a[1] = 100' value of a = [10, 20, 30, [500, 4000, 700]] identity of a = 126803974160848 value of b = [10, 20, 30, [500, 4000, 700]] identity of b = 126803974160848
a = bを実行した後に、 a または b を変更すると、 a と b は 同じものを指し示しているので、 a も b も変更されるでござる。
一次元のリストについて、 shallow copy を行ってみるでござる。以下のコー ドを用意してみたでござる。
実行結果は以下の通りでござる。#!/usr/pkg/bin/python3.9 # # Time-stamp: <2022/08/20 15:47:06 (CST) daisuke> # print ("executing 'a = [ 10, 20, 30 ]'") a = [ 10, 20, 30 ] print (" value of a =", a) print (" identity of a =", id (a) ) print ("executing 'b = a.copy ()'") b = a.copy () print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) ) print ("executing 'a[1] = 100'") a[1] = 100 print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) )
一次元のリストについて% ./test_copy_03.py executing 'a = [ 10, 20, 30 ]' value of a = [10, 20, 30] identity of a = 132150259524688 executing 'b = a.copy ()' value of a = [10, 20, 30] identity of a = 132150259524688 value of b = [10, 20, 30] identity of b = 132150259524048 executing 'a[1] = 100' value of a = [10, 100, 30] identity of a = 132150259524688 value of b = [10, 20, 30] identity of b = 132150259524048
.copy ()メソッドを使って、 shallow copy を行うと、新たにできた b というリストの実体は a の実体とは別のも のとなるようでござる。 a のなかの要素の値が b にコピーされるでござる。 a と b は異なるオブジェクトなので、 a の要素を変更しても、 b の要素は 変更されないでござる。
次に、多次元のリストに対して、 shallow copy を行ってみるでござる。以下 のコードを用意したでござる。
実行結果は以下の通りでござる。#!/usr/pkg/bin/python3.9 # # Time-stamp: <2022/08/20 15:47:28 (CST) daisuke> # print ("executing 'a = [ 10, 20, 30 ]'") a = [ 10, 20, 30, [500, 600, 700] ] print (" value of a =", a) print (" identity of a =", id (a) ) print ("executing 'b = a.copy ()'") b = a.copy () print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) ) print ("executing 'a[3][1] = 4000'") a[3][1] = 4000 print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) )
shallow copy だと、リストのなかのリストについては、値ではなく reference が渡されるため、コピーして新たなオブジェクトを作ったあとに、 リストのなかのリストの要素を変更すると、 a も b も変更されるでござる。% ./test_copy_04.py executing 'a = [ 10, 20, 30 ]' value of a = [10, 20, 30, [500, 600, 700]] identity of a = 130015449133520 executing 'b = a.copy ()' value of a = [10, 20, 30, [500, 600, 700]] identity of a = 130015449133520 value of b = [10, 20, 30, [500, 600, 700]] identity of b = 130015437586128 executing 'a[3][1] = 4000' value of a = [10, 20, 30, [500, 4000, 700]] identity of a = 130015449133520 value of b = [10, 20, 30, [500, 4000, 700]] identity of b = 130015437586128
多次元のリストに対して、 deep copy を行ってみるでござる。以下のコード を用意したでござる。
実行結果は以下の通りでござる。#!/usr/pkg/bin/python3.9 # # Time-stamp: <2022/08/20 15:48:00 (CST) daisuke> # # importing copy module import copy print ("executing 'a = [ 10, 20, 30 ]'") a = [ 10, 20, 30, [500, 600, 700] ] print (" value of a =", a) print (" identity of a =", id (a) ) print ("executing 'b = copy.deepcopy (a)'") b = copy.deepcopy (a) print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) ) print ("executing 'a[3][1] = 4000'") a[3][1] = 4000 print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) )
deep copy の場合は、コピーした後に、一方のリストのなかのリストの要素を 変更しても、他方が変更されることはないでござる。% ./test_copy_05.py executing 'a = [ 10, 20, 30 ]' value of a = [10, 20, 30, [500, 600, 700]] identity of a = 137240115644560 executing 'b = copy.deepcopy (a)' value of a = [10, 20, 30, [500, 600, 700]] identity of a = 137240115644560 value of b = [10, 20, 30, [500, 600, 700]] identity of b = 137240118548432 executing 'a[3][1] = 4000' value of a = [10, 20, 30, [500, 4000, 700]] identity of a = 137240115644560 value of b = [10, 20, 30, [500, 600, 700]] identity of b = 137240118548432
shallow copy の場合においても、 copy モジュールを使うことができるでご ざる。
実行結果は以下の通りでござる。#!/usr/pkg/bin/python3.9 # # Time-stamp: <2022/08/20 15:48:24 (CST) daisuke> # # importing copy module import copy print ("executing 'a = [ 10, 20, 30 ]'") a = [ 10, 20, 30 ] print (" value of a =", a) print (" identity of a =", id (a) ) print ("executing 'b = copy.copy (a)'") b = copy.copy (a) print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) ) print ("executing 'a[1] = 100'") a[1] = 100 print (" value of a =", a) print (" identity of a =", id (a) ) print (" value of b =", b) print (" identity of b =", id (b) )
% ./test_copy_06.py executing 'a = [ 10, 20, 30 ]' value of a = [10, 20, 30] identity of a = 129258636656912 executing 'b = copy.copy (a)' value of a = [10, 20, 30] identity of a = 129258636656912 value of b = [10, 20, 30] identity of b = 129258636659152 executing 'a[1] = 100' value of a = [10, 100, 30] identity of a = 129258636656912 value of b = [10, 20, 30] identity of b = 129258636659152