はじめに
いなたつアドカレの二十一日目の記事です。
今日はちょっとPythonのオブジェクトIDと 参照渡し 変数のお話を
普段Pythonをあまり使わない人間なので間違ってる部分勘違いしてる部分ありましたら優しく訂正をお願いします。。。。。。
Python's Tips
- Pythonは全てがオブジェクト
- オブジェクトにはそれぞれオブジェクトIDがふられている
- 変数にはオブジェクトIDが代入される
- 関数呼出し時の引数(変数)にもオブジェクトIDが渡される
オブジェクトのIDを確認する
num1 = 1
num2 = 2
print(id(1)) # 1
print(id(num1)) # 1
print(id(1 + 1)) # 2
print(id(num1 + 1)) # 2
print(id(num2)) # 2
1と2のIDをいろんな方法でだしてみました。
$ python test.py
140280906740832
140280906740832
140280906740864
140280906740864
140280906740864
こんな感じの出力になりました。
もう一度実行してみます。
$ python test.py
140040431375456
140040431375456
140040431375488
140040431375488
140040431375488
順番的には上から1,1,2,2,2のIDが出力されていますが、1回目と2回目で出力されている値はちがいますね。
そして1とnum1のオブジェクトIDが同じですね。
ここからPythonのオブジェクトIDは動的に決まっており、1と1を入れた変数のオブジェクトIDが共通であることがみて取れます。
では次の実験です。
num1 = 1
num2 = 2
print(id(1))
print(id(num1))
num1 += 1
print(id(num1))
print(id(num2))
実行結果
$ python test2.py
140413757891680
140413757891680
140413757891712
140413757891712
2つめと3つめの間でnum1が1から2に変わってますね、ここでオブジェクトIDも変わってます。
次の実験
num = 1
list = [1]
print(num)
print(id(num))
num = 2
print(num)
print(id(num))
print(list)
print(id(list))
list[0] = 2
print(list)
print(id(list))
実行結果
$ python test3.py
1
140609807909984
2
140609807910016
[1]
140609798595120
[2]
140609798595120
はい、数字は1から2に変わるとオブジェクトIDが変わってますが、リストは中の値が変わってもオブジェクトIDが変わりません。
関数に渡す
number_list = [1,2,3,4,5,6,7,8,9,10]
def show (arg):
print(arg)
print(id(arg))
def update_list (arg):
arg[0] = 0
print(id(number_list))
show(number_list)
update_list(number_list)
show(number_list)
実行結果
$ python test4.py
140048610816560
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
140048610816560
[0, 2, 3, 4, 5, 6, 7, 8, 9, 10]
140048610816560
関数に渡しても中身を書き換えてもオブジェクトIDが変わってないことがわかりますね。
まとめ
Pythonは 参照渡し オブジェクトIDを渡しており、数字などのイミュータブルな値は それぞれ固有のオブジェクトIDが存在するため、関数内部で値を書き換えると参照しているオブジェクトIDが変わるため、参照を渡してはいるものの実質値渡しのような挙動をする。 変数に代入することしかできず、変数の代入は別のオブジェクトIDを代入することになり、呼び出し元のオブジェクトには影響を与えない。
リストなどのミュータブルなオブジェクトの内部状態を変更すると、リストそのもののオブジェクトIDが変化するわけではないので、関数内部での変更は同一オブジェクトIDを持つ変数やオブジェクトに影響を与える。もちろん、変数に別のリストを代入すれば別のオブジェクトIDになり、外部のオブジェクトには影響を与えない。