ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【python】pythonにおける参照渡し/値渡し

 2019/05/20:色々と間違えていたので修正しました。

 今回は割とハマりがちなpythonにおける参照渡しと値渡しについてです。
 関数の引数として渡した場合はどちらなのか? みたいな内容の備忘録です。

 自分も結構はまってしまったので何かの助けになれば幸いです。

 では始めます。


1:pythonはすべて参照渡し
 pythonの処理としては、すべて参照渡しになっています。
 しかし渡された値を変更した際に元の値自体も変更されてしまうかどうかは、以下のようにオブジェクトの型に依存します。

変更不可(Immutable)な型 変更可能(Mutable)な型
int, float, str, tuple, bytes, frozenset 等 list, dict, set, bytearray 等

 例えば引数にintやstrなどを渡してその関数内で書き換えられたとしても、呼び出し元の変数の値には変化はありません。
 しかしlistなどを引数にして関数内で書き換えられた場合は、呼び出し元の変数の値も書き換えられてしまいます。


2:変更可能な型を別オブジェクトとしてコピーしたい場合
 変更可能な型だけど変更されたくない場合は、deepcopy関数を使って別オブジェクトとしてコピーします。

import copy

b = copy.deepcopy(a) # aとbは別オブジェクトになる


3:変更不可な型と変更可能な型のサンプルコード
 pythonでの変更不可な型と変更可能な型がわかったのでそれぞれ軽く検証できるコードを作成しました。

・sampleCode.py

import copy

def main():
    # 変更不可の型の場合
    a = 1
    print("a:", a)
    change_argument_int(a)
    print("a:", a)

    # 変更可能の型の場合
    intList = [1, 2, 3]
    intList2 = intList # 参照渡しなのでintListが変更されるとintList2も変更される
    print("intList:", intList)
    change_argument_list(intList)
    print("intList2:", intList2)

    # 変更可能の型でdeepcopyを使って別オブジェクトにした場合
    intList3 = [1, 2, 3]
    intList4 = copy.deepcopy(intList3) # 別オブジェクトになる
    print("intList3(1st):", intList3)
    change_argument_list(intList3)
    print("intList3(2nd):", intList3)
    print("intList4:", intList4)

# 引数の中身を書き換える(int型なので呼び出し元に影響なし)
def change_argument_int(arg):
    arg = 200

# 引数の中身を書き換える(list型なので呼び出し元に影響あり)
def change_argument_list(intList):
    intList.append(10)

if __name__ == '__main__':
    main()

 上記を実行すると以下のように、変更不可な型と変更可能な型のそれぞれの場合で動いていることがわかります。

a: 1
a: 1
intList: [1, 2, 3]
intList2: [1, 2, 3, 10]
intList3(1st): [1, 2, 3]
intList3(2nd): [1, 2, 3, 10]
intList4: [1, 2, 3]


 以上がpythonにおける参照渡し/値渡しについてです。

 初期化の時の型だったり、引数の型だったりで意図しない動きになる場合があるので注意が必要です。(自戒も含めて…)


・参考資料