参照渡しだが値は保護
以下のコードを実行すると、関数実行の前後で引数に渡した変数の値が影響を受けていない。挙動だけ見ると値渡し(call by value)だが、Pythonの関数引数は参照渡し(call by reference)だという。
1 2 3 4 5 6 7 8 9 10 11 |
def func(arg): arg += 1 a = 0 func(a) print(a) func(a) print(a) # 0 # 0 |
実際には、引数を受け取った段階では、元の引数オブジェクトへの参照が渡されているが、その内容が変更されたときに新しいオブジェクトが生成され、元の変数の内容は保たれるらしい。
だが、時には関数に渡した引数の内容を、関数で変更したいような時がある(たとえばシステムの状態に関するフラグの変更など)。そのような変数をglobal
定義してもよいが、やはり全体系への汚染のことを考えると気持ちはよくない。
リストを使った場合
以下の例では、要素を1つ持つリストをつくり、そのリストを関数に渡して、関数内でリストの要素の値を変更している。
1 2 3 4 5 6 7 8 9 10 11 |
def func(lst): lst[0] += 1 a = [0] func(a) print(a) func(a) print(a) # [1] # [2] |
リストなどのミュータブルなオブジェクトの場合、その内容が関数内での変更の影響を受けている。
クラスを使った場合
以下の例では、カウンタをプロパティに持つクラスをつくり、そのインスタンスを関数に渡して、関数内でインスタンスのカウンタの値を変更している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class ArgClass: def __init__(self): self.counter = 0 def func(cl): cl.counter += 1 a = ArgClass() func(a) print(a.counter) func(a) print(a.counter) # 1 # 2 |
変数の内容を特定の関数で変更させたい場合、リストよりもクラスを使った方が意味づけができてよいのではないか。