1.配列を関数内で書き換えたとき、関数呼び出し後に配列はどうなる?
以下の状況を想定する。
- 関数
test(array)
に配列array
を引数として渡す - 関数
test(array)
内で、何らかの方法で配列の要素を増やす - 関数
test(array)
呼び出し後に、配列array
の中身を確認する
ここでは、関数呼び出し後にarray
の中身が書き変わっているかどうかを考える。
コードで書くと次のようになる。
def test(array):
#ここでarrayに[3]を追加して[1,2,3]にする
return sum(array)
array = [1,2]
ans = test(array)
print(array) # 結果は[1,2]か?、それとも[1,2,3]か?
print(ans) #6
今回は、
- 関数呼び出し後に
array
の中身が書き変わってしまうパターン - 関数呼び出し後も
array
の中身が書き変わらないパターン
をそれぞれ2つずつ紹介する。
2. 関数呼び出し後にarrayの中身が書き変わってしまうパターン
2.1. 書き変わるパターンその1 : append()を使う
def test(array):
array.append(3) #aの要素を増やす
return sum(array)
array = [1,2]
ans = test(array)
print(array) #[1,2,3]と表示される(!)
print(ans) #6
関数test(array)
を実行すると、array
が書き変わってしまうことがわかる。これは、関数test(array)
に対して配列array
が参照渡しされていることが原因である。関数test()
は配列array
そのものを参照するため、関数内でappend
すると、関数の呼び出し後にはarray
は書き変わってしまう。
2.2. 書き変わるパターンその2 : +=を使う
def test(array):
array += [3] #aの要素を増やす
return sum(array)
array = [1,2]
ans = test(array)
print(array) #[1,2,3]と表示される(!)
print(ans) #6
この場合も同様。関数の呼び出し後にはarray
は書き変わってしまう。
+=
は実質append()
と同じで、array
の要素を一つ追加すると考えてよい。
3. 関数呼び出し後もarrayの中身が書き変わらないパターン
上記のような、関数内での配列操作が外に伝わってしまう状況を回避するには、要は関数内でarray
のコピーを作ってから作業を始めれば良い。
3.1. 書き変わらないパターンその1 : copy()を使う
pythonでは、配列の中身を丸ごと値渡しでコピーするcopy.copy()
とcopy.deepcopy()
が用意されている。それぞれ、いわゆる「浅いコピー」と「深いコピー」に対応する。よくわからなければとりあえずdeepcopy()
を使っておけば間違いない(たぶん…)。
import copy
def test(array):
array = copy.deepcopy(array)
array = array + [3] #aの要素を増やす
return sum(array)
array = [1,2]
ans = test(array)
print(array) #[1,2]と表示される
print(ans) #6
確かに、関数呼び出し後もarray
の中身は書き変わっていないことがわかる。
3.2. 書き変わらないパターンその2 : array = array + [3]とする
意外にも、array = array + [3]
としても要件を満たすことができる。
def test(array):
array = array + [3] #aの要素を増やす
return sum(array)
array = [1,2]
ans = test(array)
print(array) #[1,2]と表示される
print(ans) #6
確かにarray = array + [3]
とした場合でも、関数呼び出し後もarray
の中身は書き変わっていないことがわかる。
この結果は、array += [3]
を用いた場合と対称的である(2.2.節参照)。おそらく、array += [3]
とした場合はもとの配列に要素がappend()
されるのに対し、array = array + [3]
とした場合は新たに配列が確保されることが挙動の違いを生んでいるものと思われる。紛らわしい!
4. まとめ
書き変わってほしくない配列はとりあえずdeepcopy()
しよう。
array = array + [3]
とarray += [3]
で挙動が異なるのが大変紛らわしい。