Pythonプログラミングイントロダクション(5章) エイリアシングによるオブジェクトの参照

2019–11–10

5章をみていきます。この章ではPythonの構造型と呼ばれるtuple ,list, str, rangeについてみていきながら、可変性と不変性の違いが説明されています。 また、関数を引数として渡すような高階関数についても説明されています。

特にlist型のエイリアシングについて多くの説明がこの章では割かれています。確かにプログラミングでよくミスる部分ではありますね。

エイリアシング

下記のように変数aの要素を直接変更したり、変数bに対して副作用のあるappend関数を用いて値を変更すると、cの値も変わってしまいます。

a = [1]
b = [2]
c = [a , b]
print('c : ' , c)

a[0] = 0
b.append(3)
print('c : ' , c)	  
>> c :  [[1], [2]]
>> c :  [[0], [2, 3]]	  

上記の場合a,b が参照しているリストと、a,b を構成要素として生成されたcが参照しているリストが同一のものになっています。 a,bを通して参照したリストとcを通して参照しているリストは、別の経路から同じリストを参照している状態になっています。

c = [a[:] , b[:]] のようにスライスを使うと、リストは複製されるため変更してもcの値に影響しません。 a,bが参照しているリストと同じ値のリストを複製しますが、参照しているリストは同じものではなく、全く別のリストを参照している状態になります。

a = [1]
b = [2]
c = [a[:] , b[:]]
print('c : ' , c)

a[0] = 0
b.append(3)
print('c : ' , c)	  
>> c :  [[1], [2]]
>> c :  [[1], [2]]	  

Pythonではid() 関数により、オブジェクトが同じものを参照しているかを確認することができます。

a = [1]
b = a

print(id(a) == id(b))

a.append(2)

print(id(a) == id(b))

b = a[:]

print(id(a) == id(b))	  
>> True
>> True
>> False	  

要素を直接変更した場合や、append()関数を使った場合はid()関数の戻り値はは変わらないですが、スライスを使うと複製されるためにid()関数の戻り値が変わります。

まとめ

5章からエイリアシングについて取り上げました。エイリアシングはわかりにくいバグにつながるケースもあります。なのでimmutableなオブジェクトが好まれることもあります。