#Pythonでつまづいたこと
コメントでも指摘されているが、前提としてPythonにそもそも参照渡しは存在しない。以下の記事を参考。
https://qiita.com/raccy/items/d4c5e7995a8fc90109ee#_reference-de4299be58ab9d936fcb
本記事は、明らかに理解不足のときに書いたこともあって不正確な部分が散見されるが、まぁ当時どのように抱えた疑問と向き合ったのかのメモとして残しておきたい(メモを残すのはよくないのかもしれないが)。
##参照渡しとか言う謎
pythonを始めたころに、一番につまづいたのが、いたるところでよく見られる「pythonの引数は全て参照渡しである」という謎の説明である。参照渡し自体はなんとなく理解できた。ただ、pythonで以下のコードを動かすと、、、
>>> a=1
>>> b=a
>>> b=2
>>> print(a,b)
1,2
となる。 まぁ当たり前という気がするが、参照渡しだとすると納得がいかない(そもそも参照渡しではなかったのだから納得が行かないのは当然でであったようだ。。)。 何故なら、参照渡しなら
>>> a=1
>>> b=a
>>> b=2
>>> print(a,b)
2,2
となるはずだからだからである(当時のこの理解はあっていたっぽい)。「参照渡しで b=aなら、bもaも同じメモリを共有しててbが書き換わったらaも書き換わるんじゃないの?」と思っていたのである。実際、listで似たようなことをするとそうなる。
>>> a=[1,2,3]
>>> b=a
>>> b[0]=5
>>> print(a,b)
#[5,1,2],[5,1,2]
参照渡しなら、そうなるだろうという挙動である。ってことは、複数の要素が入りうるものは参照渡しで、数値型だけ違うのかと思って,以下のコードでa,bのメモリの場所を調べてみると、、、、
>>> a=1
>>> b=a
>>> print(id(a)==id(b))
True
>>> b=2
>>> print(id(a)==id(b))
False
ということで b=a とした時点ではbもaも同じ場所を参照していることになる。が、b=2とすると、bは新たに作られた2という値を持つint objectの場所を参照するようになる。一方で、aは元の場所を参照し続ける。なるほど納得。しかし、今度はlistの例の方が分からなくなる。そこで、調べてみると
>>> a=[1,2,3]
>>> b=a
>>> a_0=a[0]
>>> b[0]=5
>>> print(a[0])
5
>>> print(id(a)==id(b))
True
>>> print(id(a[0])==id(a_0))
False
>>> b=[4,5,6]
>>> print(id(a)==id(b))
False
これでやっと納得。つまり。
- b=aの時点ではbもaと同じ場所を参照する
- b[0] = 5とすると新たに5という値を持つint objectが作られて、b[0]は新たにその場所を参照するようになる。
- このとき、aもbも同じ場所を参照しているため、b[0]が新たに参照した場所をa[0]も参照する。
- b=[4,5,6]とすると、新しいlist オブジェクトが作成されるためbはその場所を参照する。このときaはもとの場所を参照し続ける。
という挙動になっているようだ。
長々と書いたけど、つまり、
変数に新たなオブジェクトそのものが代入されるときには、これまでの参照先から変わって新たなオブジェクトが作られた場所を参照するようになる。
で実務的には問題ないはず。