Pythonの文法

はじめに

プログラミング言語のPython(パイソン)を勉強しています。文法の中でややこしくて理解しずらいテーマが3点ありました。1点目はリストにおける代入とコピーの違い、2点目はミュータブルとイミュータブルの違い、3点目は浅いコピーと深いコピーの違いです。教科書の文章だけで理解しようとしても無理があるので、イラストでの説明を試みました。

3項目それぞれで、ソースコードは類似していてもプログラムの挙動は全く異なるという特徴があります。そこで、2つのソースコードを対比し、プログラムの挙動をイラストで表し、差異を明確にしました。イラストは、厳密なプログラムの挙動を表しているわけではありません。しかし、イラストとソースコードをじっくり見比べると、概要を把握できると思います。

ソースコードを読むときの注意点

Pythonにおいて、変数名はオブジェクトを指す名前です。変数はオブジェクトのデータを持たず、データのメモリ位置を持っています。したがって、ソースコードを読むとき、次の2点に注意する必要があります。1点目は、異なる変数名が同じオブジェクトを指す状況があることです。2点目は、ある変数を通してオブジェクトを変更すると、その影響が別の変数に出る場合と出ない場合の両方があることです。

オブジェクトはコンピュータのメモリ内に存在します。id(x)は、変数xが指すオブジェクトのメモリ位置を返します。メモリ位置は、オブジェクトがメモリ内のどの位置に存在するかを表す住所のようなものです。変数xとyに対して、id(x)とid(y)の値を比較することで、xとyが同じオブジェクトを指しているかいないかを判断できます。

リストにおける代入とコピー

「y = x」と「y = x[:]」は見た目が似ていますが、意味は全く異なります。代入文「y = x」の意味は、変数xが指すオブジェクトを変数yも指すようにするです。リストのコピー文「y = x[:]」の意味は、変数xが指すオブジェクトをコピーし、そのコピーされたオブジェクトをyが指すようにするです。

===== list_assign.py リストの代入 =====

===== list_assign.pyの出力 =====
===== list_copy.py リストのコピー =====

===== list_copy.pyの出力 =====

リストの代入(list_assign.py)
xとyは同じオブジェクトを指しています。x[2]を’cup’に変更すると、その影響がyに現れます。

リストのコピー(list_copy.py)
xの変更前からxとyは異なるオブジェクトを指しています。x[2]を’cup’に変更しても、yに影響しません。

ミュータブルとイミュータブル

オブジェクトの値を変更することが不可能なことを、イミュータブルといいます。イミュータブルなオブジェクトの例として、数値や文字列等があります。オブジェクトの値を変更することが許可されていることをミュータブルといいます。ミュータブルなオブジェクトの例として、リストや辞書型等があります。

===== mutable.py ミュータブル =====

===== mutable.pyの出力 =====
===== immutable.py イミュータブル =====

===== immutable.pyの出力 ====

ミュータブル(mutable.py)
リストはミュータブル(変更可能)です。 x[1]を変更しても、xとyは同じオブジェクトを指したままです。つまり、xを変更したことで、yに影響が出ます。

イミュータブル(immutable.py)
数値はイミュータブル(変更不能)です。xを変更すると、新しいオブジェクトが作成され、xはyと異なる新しいオブジェクトを指すようになります。つまり、xを変更しても、yに影響が出ません。

浅いコピーと深いコピー

浅いコピーと深いコピーはネスト構造のオブジェクトをコピーするときに重要になってきます。ネスト構造とは、リストの要素としてリストがあるなどの入れ子構造のことです。浅いコピーは、ネスト構造内の深さ1の浅い部分のみをコピーします。深いコピーは、ネスト構造の深い部分を含めたデータのすべてをコピーします。

リストの浅いコピーは、「y = x.copy()」,「y = list(x)」,「y = x[:]」,「y = x + []」, 「y = x * 1」で実現できます。一方、深いコピーは、copyモジュールを用いて「y = copy.deepcopy(x)」で実現できます。

===== shallow.py 浅いコピー =====

===== shallow.pyの出力 =====
===== deep.py 深いコピー =====

===== deep.pyの出力 =====

浅いコピー(shallow.py)
xとyで[2, 〇]の浅い部分はコピーされますが、[3, 4]の深い部分はコピーされません。そのため、x[0]の変更はyに影響しませんが、x[1]の変更はyに影響します。

深いコピー(deep.py)
xとyで[2, 〇]の浅い部分だけでなく、[3, 4]の深い部分もコピーされます。そのため、xを変更してもyに全く影響しません。

参考資料

喜多一著、プログラミング演習 Python 2019のPDFファイル
http://hdl.handle.net/2433/245698
本編の10.11節から10.13節まで及びコラム編の13.2節が参考になります。

作成: 藤原大樹
更新: 2020年7月3日

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です