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

提供: tknotebook
移動: 案内検索
(全てのデータはオブジェクト)
(引数は値渡し(Call By Value))
53行: 53行:
 
==引数は値渡し(Call By Value)==
 
==引数は値渡し(Call By Value)==
  
pythonで全ての引数は「値」で渡されます。Call By Value です。但し渡されるのはオブジェクトの参照です。
+
pythonでは全ての引数は'''「値」で渡されます'''。Call By Value です。但し'''渡されるのはオブジェクトの参照'''です。
  
 
ならば実質的に 参照渡し(Call By Reference)になるのではと短絡的に考える人もいるかもしれませんが、
 
ならば実質的に 参照渡し(Call By Reference)になるのではと短絡的に考える人もいるかもしれませんが、
よく考えてみましょう。一般に 参照渡し(Call By Reference)と呼ばれているもの'''「参照」'''は'''変数'''の参照です。
+
よく考えてみましょう。一般に 参照渡し(Call By Reference)と呼ばれているものの'''「参照」'''は'''変数'''の参照です。
 
オブジェクトの参照ではありません。python が値渡しで渡す参照はオブジェクトの参照です。
 
オブジェクトの参照ではありません。python が値渡しで渡す参照はオブジェクトの参照です。
  

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

メインページ>コンピュータの部屋#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にだいにゅうされている "foo" 文字列オブジェクトとは無関係だからです。

def fun(a):
  a = 2

b=1
fun(b)
print(b)

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

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


listと mutable

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


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

b = [1, 2]

fun(b)

print(b)

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

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

まとめ

まとめると

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

補足

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

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

int float str tuple bool range type(None)


その他にもこんなのがあります。

bytes complex frozenset slice type type(Ellipsis) type(NotImplemented) types.FunctionType *これは mutable, 後述します。 types.BuiltinFunctionType weakref.ref