情報処理用語の 参照渡し・参照呼出し (call by reference) の説明とは違っているので、違いを確認しておきましょう。
https://www.ap-siken.com/kakomon/25_haru/q20.html
Pythonは、すべての値がオブジェクトで、すべての変数がオブジェクトへの参照を保持し(参照型変数)、どんな変数や値であろうとメモリの参照を引数として渡し、値(オブジェクト)を共有します。
def sub(x):
print("address in x =", id(x)) # 引数xが参照しているオブジェクトのアドレス
print("x.real =", x.real) # 整数オブジェクトもアトリビュートやメソッドを持っている
x.real = 2 # 整数オブジェクトへの代入はタプルオブジェクトと同様に例外発生
a = 1
print("address of 1 =", id(1)) # 整数1オブジェクトのアドレス
print("address in a =", id(a)) # 変数aが参照しているオブジェクトのアドレス
sub(a)
address of 1 = 9793056
address in a = 9793056
address in x = 9793056
x.real = 1
/usr/lib/python3/dist-packages/requests/__init__.py:89: RequestsDependencyWarning: urllib3 (1.26.12) or chardet (3.0.4) doesn't match a supported version!
warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported "
Traceback (most recent call last):
File "t.py", line 9, in <module>
sub(a)
File "t.py", line 4, in sub
x.real = 2 # 整数オブジェクトへの代入はタプルオブジェクトと同様に例外発生
AttributeError: attribute 'real' of 'int' objects is not writable
変数の値として保持している参照を渡すのは、あくまでも「値渡し」です。Wikipediaでは「参照の値渡し」と書かれています。 「参照渡し」は、値(オブジェクト)への参照 を渡すこと(オブジェクト渡し、オブジェクト共有)ではなく、変数への参照 を渡すこと(変数渡し、変数共有、別名変数、エイリアス変数)です。 呼び出し先の関数で引数(仮引数)に直接代入すると呼び出し元の変数(実引数)に代入されるのが「参照渡し」の特徴です。 PythonやJavaは参照渡しできない言語です。 変数を関数の引数に渡す方法は以下の4種類があって、Pythonは 3 のみ、Java は 1 と 3 しかできません。
- 値型変数の値渡し
- 値型変数の参照渡し
- 参照型変数の値渡し
- 参照型変数の参照渡し
変数の型 | 渡し方 | 引数への代入 | 値(オブジェクト)への代入 | Python | Java | |
---|---|---|---|---|---|---|
1 | 値型 | 値渡し | 呼び出し元に影響しない | 呼出し元に影響しない | できない | できる |
2 | 値型 | 参照渡し | 呼び出し元に影響する | 呼出し元に影響する | できない | できない |
3 | 参照型 | 値渡し | 呼び出し元に影響しない | 呼出し元に影響する | できる | できる |
4 | 参照型 | 参照渡し | 呼び出し元に影響する | 呼出し元に影響する | できない | できない |
- 参照渡しできる言語: C++, C#, PHP, Visual Basic, FORTRAN, etc.
- 参照渡しできない言語: C, Java, JavaScript, Python, Ruby, etc.
前提として、Python では引数は代入によって渡されます。代入はオブジェクトへの参照を作るだけなので、呼び出し元と呼び出し先にある引数名の間にエイリアスはありませんし、参照渡しそれ自体はありません。
#include <iostream>
void sub(int x, int& y) { // 引数xは値渡し、引数yは参照渡し
x = 3; // 値渡しは、引数に代入したときに呼び出し元に影響しない
y = 4; // 参照渡しは、引数に代入したときに呼び出し元に影響する
}
int main(int argc, char **argv) {
int a = 1;
int b = 2;
sub(a, b);
std::cout << "a = " << a << std::endl;
std::cout << "b = " << b << std::endl;
}
a = 1
b = 4
<?php
function sub($w, &$x, $y, &$z) { // &を付けると参照渡し
$w = 5; // 値型の値渡しなので影響しない
$x = 6; // 値型の参照渡しなので影響する
$y[0] = 7; // 値型の値渡しなので影響しない
$y = [8]; // 値型の値渡しなので影響しない
$z[0] = 9; // 値型の参照渡しなので影響する
$z = [10]; // 値型の参照渡しなので影響する
}
$a = 1;
$b = 2;
$c = [3]; // PHPの配列は値型
$d = [4];
sub($a, $b, $c, $d);
print "\$a = $a\n";
print "\$b = $b\n";
print "\$c[0] = $c[0]\n";
print "\$d[0] = $d[0]\n";
$a = 1
$b = 6
$c[0] = 3
$d[0] = 10
def sub(w, x, y, z): # 参照渡しできない、参照渡し記法がない
w = 5 # 参照型の値渡しで引数への代入なので影響しない
x = 6 # 参照型の値渡しで引数への代入なので影響しない
y[0] = 7 # 参照型の値渡しでオブジェクトへの代入なので影響する
y = [8] # 参照型の値渡しで引数への代入なので影響しない
z[0] = 9 # 参照型の値渡しでオブジェクトへの代入なので影響する
z = [10] # 参照型の値渡しで引数への代入なので影響しない
a = 1
b = 2
c = [3]
d = [4]
sub(a, b, c, d)
print(f"a = {a}")
print(f"b = {b}")
print(f"c[0] = {c[0]}")
print(f"d[0] = {d[0]}")
a = 1
b = 2
c[0] = 7
d[0] = 9
y[0] = 7
は 変数y
に代入されているオブジェクトを参照し、オブジェクトの0番目要素に7
を代入します。
Pythonでは、y.__setitem__(0, 7)
とメソッド呼び出しに置き換えられ、オブジェクト自身が要素の値を変更します。
>>> c = [3]
>>> c
[3]
>>> c.__setitem__(0, 7)
>>> c
[7]
>>> c[0]
7
>>> c.__getitem__(0)
7