「Pythonの引数の引き渡し」の版間の差分

提供: tknotebook
移動: 案内検索
(補足)
(Nakamuri がページ「Pythonの引数の引き渡し方」を「Pythonの引数の引き渡し」に、リダイレクトを残さずに移動しました)
(相違点なし)

2018年9月11日 (火) 06:19時点における版

メインページ>コンピュータの部屋#Python>Python Tips


pythonは大変わかりやすい言語ですが、ひとつわかりにくい基本的な事項があります。 関数への引数の引き渡し方です。

引数の引き渡し方といえば

  1. 値渡し(Call By Value)
  2. 参照渡し(Call By Reference)

の2種類が定番ですが、pythonでは見かけ上全く同じ書き方でこの2種類が入り混じります。

例えば

def fun(a):
  a = "foo"

b="bar"
fun(b)
print(b)

を実行すると

bar

と表示されて 値渡しは明らかです。

しかし

def fun(a):
  a.append(3)

b = [1, 2]

fun(b)

print(b)

は [1, 2, 3] を出力します。

このあたりの違いはどうなっているのでしょうか?

ここではこの点について解説します。

全てのデータはオブジェクト

pythonでは全てのデータはオブジェクト型です。変数はオブジェクトの「参照」を保持します。

int や float や str もオブジェクトで変数はその値を直接保持していないのです。

引数は値渡し(Call By Value)

pythonでは全ての引数は「値」で渡されます。Call By Value です。但し渡されるのはオブジェクトの参照です。

ならば実質的に 参照渡し(Call By Reference)になるのではと短絡的に考える人もいるかもしれませんが、 よく考えてみましょう。一般に 参照渡し(Call By Reference)と呼ばれているものの「参照」変数の参照です。 オブジェクトの参照ではありません。python が値渡しで渡す参照はオブジェクトの参照です。

整数や文字列と immutable

一度作成されると変更できないオブジェクトを immutable と言います。python では基本的な数値型(int, float), str型、bool型はimuutableです。

imuutableなオブジェクトを関数の引数に渡すと 実質的に値渡しになります。

def fun(a):
  a = "foo"

b="bar"
fun(b)
print(b)

で、bは文字列オブジェクト"bar" の参照を保持しますが、仮引数a にわたるのは、bではなくbの保持する参照です。 aに "foo" 文字列オブジェクトの参照を代入しても、bの参照している文字列オブジェクトはもちろん変更されません。

def fun(a):
  a = 2

b=1
fun(b)
print(b)

も 2 ではなく 1 を表示します。b には 1への参照が代入されており、それの参照が引数として fun関数のローカル変数 a に渡されますが、ローカル変数 a が指す参照が 2 になっても グローバル変数 b には全く影響しません。

つまり、int や float のような immutable なオブジェクトは絶対に変更できないので、変数の指す「値」を変えるということは新たなオブジェクトを作成してその参照を代入するということです。引数への代入は、引数を通じて関数から呼び出し元へ伝わることはないので、引数は値渡しで渡されると考えてよいわけです。

参照渡しと mutable

pythonには内容を変更可能なオブジェクトもあります。もっともよく使うのは list型ではないでしょうか?

def fun(a):
  a.append(3)

b = [1, 2]

fun(b)

print(b)

上の場合 [1,2,3]と表示されますが、関数 fun での引数 a への操作が、関数外へ影響するのは、ローカル変数に別の新たなオブジェクトの参照を代入していないからです。

つまり、グローバル変数 b と ローカル変数 a は同じオブジェクトの参照を持っているので、ローカル変数a のもつ参照を使って、オブジェクトの「中身」を変更すると、それはグローバル変数 b 空も見ることができます。同じオブジェクトを指しているので当然でしょう。

このように mutable なオブジェクトの参照を仮引数で受け、仮引数の持つ参照を上書きすることなく、仮引数の指すオブジェクトを変更すると、呼び出し元が引数として与えたオブジェクトを変更できます。これは他の言語の「参照渡し」と同じような動きといえます。

まとめ

まとめると

  1. immutableなオブジェクトは実質的に値渡しです。
  2. mutable なオブジェクトは、仮引数を別のオブジェクトの参照で上書きしない限り、仮引数を通じてオブジェクトに行った変更を呼び出し側に伝えることができます。

補足1

pythonのオブジェクトは immutableだけど内部に mutable なオブジェクトを抱えることができます。例えば tuple は immutable ですが list は mutable なので

a=(1, 2, [3, 4]) の a[2].appen(5) は実行できます。つまり immutable なオブジェクトを引数で渡しても、その中の mutable なオブジェクトを変更することで実質的かつ部分的に参照渡しを実現できます。ご注意ください。

補足2

基本的な immutableな型

int, float, str, tuple, bool, range, type(None)


その他の immutableな型

bytes, complex, frozenset, slice, type, type(Ellipsis), type(NotImplemented), types.FunctionType, types.BuiltinFunctionType, weakref.ref