Python 関数の引数は値渡し?参照渡し?
ここでは Python 関数の引数の渡し方についてご説明します。
引数の「値渡し」「参照渡し」とは?
プログラミングで関数に引数を渡す時、「値渡し」と「参照渡し」という渡し方があります。
「値渡し(call by value)」というのは、関数には値のコピーが渡され、関数の中でその値が変更されても、関数の呼び元の変数は変更されない引数の渡し方です。
「参照渡し(call by reference)」というのは、そのオブジェクトへの参照が関数に渡され、関数内でその値が変更されると、関数の呼び元の変数も変更される引数の渡し方です。
Python 公式ドキュメントの引数に関する記述
Python 公式ドキュメントの「4.6. 関数を定義する」では引数の渡し方について、次のように書かれています。
引数は 値渡し (call by value) で関数に渡されることになります (ここでの 値 (value) とは常にオブジェクトへの 参照(reference) をいい、オブジェクトの値そのものではありません) 。
また、該当箇所に以下のような脚注が入っています。
実際には、オブジェクトへの参照渡し (call by object reference) と書けばよいのかもしれません。というのは、変更可能なオブジェクトが渡されると、関数の呼び出し側は、呼び出された側の関数がオブジェクトに行ったどんな変更 (例えばリストに挿入された要素) にも出くわすことになるからです。
Python では全て「参照渡し」と書かれているページをたまに見かけますが、一般的に言う「参照渡し」ではありません。
Python の公式ドキュメントの「プログラミング FAQ」 にも、以下のように書かれています。
出力引数のある関数 (参照渡し) はどのように書きますか?
前提として、Python では引数は代入によって渡されます。代入はオブジェクトへの参照を作るだけなので、呼び出し元と呼び出し先にある引数名の間にエイリアスはありませんし、参照渡しそれ自体はありません。 望む効果を得るためには幾つかの方法があります。
Python の「オブジェクトへの参照渡し」とは?
まとめると、Python では「オブジェクトへの参照渡し (call by object reference)」で引数が渡され、具体的にはオブジェクトの参照のコピーが仮引数に代入された状態です。
ですので、関数内での変更が関数の外でも有効になるかどうかは、引数で渡されたオブジェクトがミュータブルかイミュータブルかで違ってきます。
イミュータブルなオブジェクトというのは一度生成したら変更できないオブジェクトで、ミュータブルなオブジェクトは生成された後で変更できるオブジェクトです。
例えば、文字列はイミュータブル(生成後に変更不可)なオブジェクトなので、引数で渡された文字列の参照が入っている変数に、関数内でその変数に文字列を代入した時、同じ参照のまま値が変更されるわけではなく、新しい文字列の参照が変数に代入されます。
そのため、関数内で値を変更しても、引数として渡された元の参照を持つ文字列は変更されないということです。
こちらは、Python のイミュータブル(変更不可)とミュータブル(変更可)な代表的なオブジェクトです。
イミュータブル(変更不可)オブジェクト
- bool - ブール値
- int - 整数
- float - 浮動小数点数
- str - 文字列
- tuple - タプル
- bytes - バイト
- frozenset - フローズンセット
ミュータブル(変更可)なオブジェクト
- list - リスト
- dict - 辞書
- bytearray - バイト配列
- set - セット
イミュータブルとミュータブルな引数の受け渡しの例
試しに、イミュータブルなオブジェクトの文字列と、ミュータブルなオブジェクトのリストで関数内で値を変更した際の違いを確認してみましょう。
まずは文字列を引数として渡して値を変更してみます。
def func(value):
value = "zzz"
print(f"value = {value} - in func()")
value = "aaa"
print(f"value = {value} - before func()")
func(value)
print(f"value = {value} - after func()")
実行結果は次のようになり、関数内での変更は関数の外には反映されていないことがわかります。
value = aaa - before func()
value = zzz - in func()
value = aaa - after func()
今度はリストを引数として渡して値を変更してみます。
def func(value):
value[0] = "aa"
value.append("d")
print(f"value = {value} - in func()")
value = ["a","b","c"]
print(f"value = {value} - before func()")
func(value)
print(f"value = {value} - after func()")
実行結果は次のようになり、関数内での変更が関数の外でも反映されていますね。
value = ['a', 'b', 'c'] - before func()
value = ['aa', 'b', 'c', 'd'] - in func()
value = ['aa', 'b', 'c', 'd'] - after func()
イミュータブルな引数の関数内での変更を、関数の外に反映させたい時は、return で戻り値を返して代入するしかありません。
以上、Python 関数の引数の渡し方についてご説明しました。