Notebook

これは日々の作業を通して学んだことや毎日の生活で気づいたことをを記録しておく備忘録である。

HTML ファイル生成日時: 2024/11/21 17:40:55.112 (台灣標準時)

Python の shallow copy と deep copy

"The Python Standard Library" に書いてあったこと

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 の例

一次元のリストについて、 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) )

実行結果は以下の通りでござる。

% ./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

shallow copy だと、リストのなかのリストについては、値ではなく reference が渡されるため、コピーして新たなオブジェクトを作ったあとに、 リストのなかのリストの要素を変更すると、 a も b も変更されるでござる。

deep copy の例

多次元のリストに対して、 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) )

実行結果は以下の通りでござる。

% ./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

deep copy の場合は、コピーした後に、一方のリストのなかのリストの要素を 変更しても、他方が変更されることはないでござる。

shallow copy の方法について

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

参考文献



Frequently accessed files

  1. Computer___Python/20220518_0.html
  2. Computer___Network/20230726_00.html
  3. Misc___Taiwan/20240207_00.html
  4. Computer___Network/20230516_00.html
  5. Computer___FreeBSD/20220621_0.html
  6. Computer___Python/20220715_0.html
  7. Computer___Network/20230508_00.html
  8. Food___Taiwan/20220429_0.html
  9. Computer___NetBSD/20220817_3.html
  10. Computer___Python/20220410_0.html
  11. Computer___Network/20240416_00.html
  12. Computer___Network/20240130_00.html
  13. Computer___Debian/20210223_1.html
  14. Computer___NetBSD/20230119_00.html
  15. Computer___Python/20210124_0.html
  16. Computer___Python/20221013_0.html
  17. Computer___NetBSD/20220818_1.html
  18. Computer___NetBSD/20220428_0.html
  19. Science___Math/20220420_0.html
  20. Computer___NetBSD/20240101_02.html
  21. Computer___NetBSD/20220808_0.html
  22. Computer___TeX/20230503_00.html
  23. Computer___NetBSD/20230515_00.html
  24. Science___Astronomy/20220503_0.html
  25. Computer___NetBSD/20210127_0.html
  26. Computer___Python/20240101_00.html
  27. Computer___Network/20220413_1.html
  28. Computer___Python/20220816_1.html
  29. Computer___NetBSD/20210204_0.html
  30. Travel___Taiwan/20220809_2.html


HTML file generated by Kinoshita Daisuke.