Pythonでコード書いてて「マジかよ」と思ったんだけど:
def wrap_list(value, base_list = []):
base_list.append(value)
return base_list
みたいなコードがあるとする。これを第2引数を省略して何度も呼び出すと:
print(wrap_list(1)) # => [1]
print(wrap_list(2)) # => [1, 2]
という結果になる。2回目は[2]となってほしいのに。
試しにid関数でオブジェクトIDを表示してあげるとどうなっているかよく分かる。
def wrap_list(value, base_list = []):
print(id(base_list)) # debug print
base_list.append(value)
return base_list
wrap_list(1) # => 4373211008
wrap_list(2) # => 4373211008
どちらの呼び出しにおいても、base_listのオブジェクトIDは同一。つまり、呼び出しごとに空のリストが生成されているわけではなく、たった1度だけ生成されている、ということになる。
ちなみにRubyでも試してみたが:
def wrap_list(value, base_list = [])
puts base_list.object_id
base_list << value
end
wrap_list(1) # => 70218064569220
wrap_list(2) # => 70218064588580
こっちは呼び出しごとに生成されているっぽい。
Pythonで上記のようなコードを書くことはないと思うが:
class WrappedList(object):
def __init__(self, initial = []):
self.list = initial
def append(self, value):
self.list.append(value)
みたいなケースでも当然同様のことが起きるので、注意する必要はありそう。
根本の問題としては、「呼び出しごとに初期化されない」という点で、回避策としては「破壊的な操作は行わない」に尽きる。もし破壊的な操作が必要なのであれば、デフォルト引数自体はNoneにしておき、Noneならば空のリストで初期化、みたいなコードにしてあげれば良さそう。