today::エンジニアに憧れる非エンジニア

今のところは、エンジニアとは言えないところの職種です。しかしエンジニア的なものの考え方に興味津津。

Pythonあれこれ 2021-01-23

概要

「Head First Python 第2版」を進めていった中で考えたことの記録です。

Head First Python 第2版 ―頭とからだで覚えるPythonの基本

Head First Python 第2版 ―頭とからだで覚えるPythonの基本

  • 作者:Paul Barry
  • 発売日: 2018/03/24
  • メディア: 単行本(ソフトカバー)

call by sharing

first = [1, 2, 3, 4, 5]
print(first)
# => [1, 2, 3, 4, 5]
second = first
print(second)
# => [1, 2, 3, 4, 5]
print(id(first))
# => 2915364366464 ※一例
print(id(second))
# => 2915364366464 ※一例

上記コードにおいて注目すべきポイントは、「id(second)の値とid(third)の値が同じである」という点です。

Pythonの代入演算子変数a = 変数bという形で使用した場合、「変数aが指すオブジェクトの別名として変数bを定義する」という動作をします。このような評価戦略は、「call by sharing」「共有渡し」「参照の値渡し」などと呼ばれます1

上記の例でいうところのid:2915364366464というオブジェクトは、「最初にfirstに代入した[1, 2, 3, 4, 5]というリスト」を指します。secondid2915364366464です。

この状態でsecond.append(6)を実行した場合の動作は以下のようになります。

second.append(6)
print(second)
# => [1, 2, 3, 4, 5, 6]
print(first)
# => [1, 2, 3, 4, 5, 6]

secondが指すリストもfirstが指すリストも同一のオブジェクトなので、そこにappend()という操作を行うと、append()の結果はsecondにもfirstにも反映される、というわけです。

なお、「secondそのものがfirstの別名とされる」わけではありません。この状態でいえば、「firstが指すオブジェクトそのものを入れ替えても、secondが指すオブジェクトはそのまま維持される2」という動作になります。

print(id(first))
# => 2915364366464 ※一例
print(id(second))
# => 2915364366464 ※一例
first = [1, 2, 3, 4, 5]
print(id(first))
# => 2915364749760 ※一例
print(id(second))
# => 2915364366464 ※一例

上記コードの注目ポイントは以下です。

  • first = [1, 2, 3, 4, 5]以前は、id(first)の値とid(second)の値が同じものであったこと
  • first = [1, 2, 3, 4, 5]以降は、id(first)の値とid(second)の値が違うものになっていること
  • first = [1, 2, 3, 4, 5]以降も、id(second)の値は変わらないこと

リストをコピーするには

リストをコピーするためのメソッドとして、リストにはcopy()というメソッドが用意されています。

print(second)
# => [1, 2, 3, 4, 5, 6]
print(id(second))
# => 2915364366464 ※一例
third = second.copy()
print(third)
# => [1, 2, 3, 4, 5, 6]
print(id(third))
# => 2915364744320 ※一例
third.append(7)
print(third)
# => [1, 2, 3, 4, 5, 6, 7]
print(second)
# => [1, 2, 3, 4, 5, 6]
print(id(second))
# =>2915364366464
print(id(third))
# => 2915364744320

上記コードの注目ポイントは以下です。

  • id(second)の値とid(third)の値が異なること
  • third.append(7)を実行した後も、secondが指すリストの内容は変わらないこと
    • id(second)の値も変わらない
  • third.append(7)を実行した後も、id(third)の値は変わらないこと

  1. 「call by sharing」などと称される評価戦略の名称については、文脈ごとに異なったものが使われ、広く一致したものがありません。

  2. なお、このような操作をした場合に「firstが指すオブジェクトそのものを入れ替えると、secondが指すオブジェクトもfirstが指すオブジェクトに入れ替えられる」という評価戦略もあり、そちらは「参照渡し(call by reference)」と呼ばれます。C#におけるinoutrefが参照渡しの例です。