10/21
shiracamusよりコメントをいただき語句を修正し、情報を追加。
コメント欄に大変わかりやすい図をいただいたので是非合わせてご覧ください。
#結論からいうと
リストのメソッドであろうと関係ない。pythonは参照が代入される。
list.append(x)
引数として渡した参照が追加される。
appendがやっているのは要素を一つ増やして、引数の参照を代入。
a[len(a):] = [x] と等価
####list.extend(iterable)
引数の配列の要素の参照が追加される。
extendがやっているのは、引数の配列長分の要素を追加し、引数の配列の要素を順番に代入。
a[len(a):] = iterable と等価
####参照先の値に依らない配列が欲しいとき
これらは新しいオブジェクトを生成する操作なので、値は同じでも参照先が異なる。
b[:]#浅いコピー
[i for i in b]#浅いコピー
import copy
copy.copy(b)#浅いコピー
copy.deepcopy(b)#深いコピー
#検証
まずはこれを見てほしい。
a = [0,1,2]
b = [3,4,5]
a.append(b)
b[1] = 'omg'
>>>[0, 1, 2, [3, 'omg', 5]]
bの要素が変更されるとaの対応する要素も変更される。
上には示していないが、逆もまた然りである。
appendがやっているのは要素を一つ増やして、引数の参照を代入しているだけなので、
ミュータブルであれば、互いの変更が反映される。
###list.extend()は浅いコピーのような振る舞い
a = [0,1,2]
b = [3,4,[5]]
a.extend(b)
b[1] = 'omg'
a
>>>[0, 1, 2, 3, 4, [5]]
#だがしかし
b[2][0] = 'omg'
a
>>>[0, 1, 2, 3, 4, ['omg']]
引数の配列の要素の参照が追加される。
extendがやっているのは、引数の配列長分の要素を追加し、順番に参照を代入。
引数の要素がイミュータブルであれば、互いの変更が反映される。
#IDを見るとわかりやすい
下記の例を見ると、参照しか渡されていないことがわかりやすい。
つまり要素がミュータブルであれば、参照先が変更されれば、listの中身も変わってしまう。
###list.append()
a = [0,1,2,3]
b = 4
print(*[id(i) for i in a])
print(id(b))
a.append(b)
print(*[id(i) for i in a])
>>>1773300720 1773300752 1773300784 1773300816
>>>1773300848
>>>1773300720 1773300752 1773300784 1773300816 1773300848
###list.extend()
a = [0,1,2]
b = [3,4,5]
print(*[id(i) for i in a])
print(*[id(i) for i in b])
a.extend(b)
print(*[id(i) for i in a])
>>>1773300720 1773300752 1773300784
>>>1773300816 1773300848 1773300880
>>>1773300720 1773300752 1773300784 1773300816 1773300848 1773300880
###ドキュメント
####list.append(x)
リストの末尾に要素を一つ追加します。
a[len(a):] = [x] と等価です。
####list.extend(iterable)
イテラブルのすべての要素を対象のリストに追加し、リストを拡張します。
a[len(a):] = iterable と等価です。
5. データ構造 — Python 3.6.5 ドキュメント
#ひとこと
考えてみるとこの仕様以外しっくりこないことはわかるし、
この仕様でないとできないプログラムもある。
だが、直観とは違う気もする。
クラスのプライベート変数などいじられたくないところは参照を切る意味でコピーして代入しよう。
そして、コーディングをするときはミュータブルかどうか常に気を遣おう。
そして、初投稿。みなさんよろしくね。
#出典・ソース
5. データ構造 — Python 3.6.5 ドキュメント