Pythonの参照渡しと(値渡し)
まずはこちらをご覧ください。
Pythonのサンプルコード書いてみました。
class Test: def __init__(self) -> None: self.list_ = [1,2,3,4] self.num = 100 def get_num(self): return self.num def get_list(self): return self.list_ test = Test() num = test.get_num() num = 200 # test.numは...?100 or 200? list_ = test.get_list() list_.append(700) # test.list_に700はいる or いない?
...num
と list_
、 test.num
と test.list_
をprintした時、何が出力されると思いますか?
ちなみに私は、
- num = 200、test.num = 100 - list_ = [1,2,3,4,700]、test.list_ = [1,2,3,4]
だと思ってました 👀
はい、これは、間違いですね ☝️
正解は...
# 出力部分の実装 print(f'test.num = {test.num}' + f'\tアドレス:{id(test.num)}') print(f'num = {num}' + f'\tアドレス:{id(num)}') print('==================================') print(f'test.list_ = {test.list_}' + f'\tアドレス:{id(test.list_)}') print(f'list_ = {list_}' + f'\tアドレス:{id(list_)}') # 結果 test.num = 100 アドレス:4332965200 num = 200 アドレス:4332968400 ================================== test.list_ = [1, 2, 3, 4, 700] アドレス:4334289664 list_ = [1, 2, 3, 4, 700] アドレス:4334289664
id
で各変数のアドレス(格納先)も出力させてみました。
こうすると、なんで num
と list_
で挙動が違うのかが、わかるかと思います。
リストの場合は、どうも参照渡しになっているのです 👀 !
(調べて分かったのですが、 num
も実は参照渡し...後述)
※ アドレスは実行環境によって変わります。
色々調べてみた結果...
Pythonには、値渡しが存在しない 👀 !?
- さっきは返り値
num
に代入していたので気づかなかったのですが、代入しないでアドレスを見ると...一緒でした 👀 タイトルの値渡しを( )にしている理由です
test = Test() num = test.get_num() # 結果 test.num = 100 アドレス:4395519312 num = 100 アドレス:4395519312
- 値が代入される時に、新しいオブジェクト(格納先)が作られて、そこに代入値が入れられるらしい
test.num = 100 アドレス:4332965200 num = 100 アドレス:4332965200 num = 200 の後↓ test.num = 100 アドレス:4332965200 num = 200 アドレス:4332968400 # アドレス違う
型によって挙動が変わるらしい
- イミュータブルな型 → 代入時に新しいオブジェクトが生成される(結果的に値渡しのような挙動になる)
- ミュータブルな型 → 参照渡しになるので、元の値も変わってしまう
- Pythonではどの型がイミュータブルなのかは一覧表↓
Pythonの組み込みデータ型の分類表(ミュータブル等) - ガンマソフト株式会社
Python 3.10ですが、公式ドキュメントにも、ちらっとイミュータブルについて説明が...
3. Data model - Python 3.10.4 documentation
オブジェクトによっては 値 を変更することが可能です。値を変更できるオブジェクトのことを mutable と呼びます。 生成後に値を変更できないオブジェクトのことを immutable と呼びます。 (mutable なオブジェクトへの参照を格納している immutableなコンテナオブジェクトの値は、 その格納しているオブジェクトの値が変化した時に変化しますが、 コンテナがどのオブジェクトを格納しているのかが変化しないのであれば immutable だと考えることができます。 したがって、immutable かどうかは値が変更可能かどうかと完全に一致するわけではありません) オブジェクトが mutable かどうかはその型によって決まります。 例えば、数値型、文字列型とタプル型のインスタンスは immutable で、dict や list は mutable です。
※ コンテナ = 任意の型のデータを複数格納できるデータ型(list, tuple, dict, set)
参考
こちらの記事を主に参考にしました!
参照についてもう少し詳しく ~PythonとJavaを例に~ - Qiita
感想
- Pythonには、値渡しが存在しないというのは知らなかったし、もはや、リストさえも値渡しだと思っていたので、色々間違って理解していたようです。
- これからリストを返り値で返すときは、参照するだけなら良いですが、変えてしまう可能性がある場合(あまり良くはない)は、新しいリストオブジェクトを作成して返すと事故らなそうですね。