Pythonにおける値渡し
Pythonでは型によって変数や関数へのデータの受け渡しが「値渡し」と「参照渡し」に異なります。
ある型の場合は値渡しになるが、別の型では参照渡しになることがあるということです。
例えば以下の例を見てみます。
def add(a): a += 1 return a val = 10 res = add(val) print('val = ' + str(val)) print('res = ' + str(res))
実行結果は以下のようになります。
val = 10 res = 11
上記の例では関数add()の中で、呼び元の変数valをaとして受け取り、受け取った変数aに変更を加えています。
そして、その結果を呼び元に返却するということをしています。
しかし、呼び元でvalをprintしてみるとわかるように、関数の実行前後で値は10のまま変わっていません。
これは、valが関数add()に値渡しで渡されているためです。
値渡しでは、呼び元の値が別の領域にコピーされて渡されます。
つまり、値自体は同じなのですが、実体は別のものが渡されるということになります。
そのため、その変数をいくら関数add()内で変更しても、呼び元のvalには影響が出ないのです。
Pythonにおける参照渡し
では、次の例を見てみましょう。
def add(a): a.append(4) return a val = [1, 2, 3] res = add(val) print('val = ' + str(val)) print('res = ' + str(res))
先ほどの例とほぼ同じです。
異なるのは、valのデータ型がintからlistになっていることです。
実行結果は以下のようになります。
val = [1, 2, 3, 4] res = [1, 2, 3, 4]
先ほどの例と異なる結果が出ました。
変更を行っているのは関数内なのにも関わらず、呼び元の変数も変わってしまっています。
これは、valが関数add()に参照渡しで渡されていることが原因です。
値渡しは、呼び元の変数をコピーして関数に渡していました。
値は同じですが、実体は異なります。
一方参照渡しというのは、変数そのものを渡します。
正確には、変数のアドレスを渡します。
なので、関数内の変更も呼び元の変数そのものに影響するわけです。
変更不可(immutable)な型と変更可能(mutable)な型
なぜ、同じ処理をしているのに挙動が異なるのでしょうか。
その原因は、データ型にあります。
最初の例ではint型で次の例ではlist型を用いています。
この差が、値渡しと参照渡しの違いを引き起こしています。
Pythonでは、データの方によって値渡しか参照渡しが異なります。
immutableな型は値渡しされ、muttableな型は値渡しされます。
具体的には、int、str、boolなどはimmutableな型で、listやdict、classなどはmuutableな型となります。
今回はあくまで使う側の立場からmuttableとimuttableの違いを説明しましたが、内部の挙動としては以下のようになっているようです。
変数に代入されているオブジェクトそれ自体を変更可能なものがmuttable、変更不可のものがimmutableと呼ぶようです。