3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Pythonのデフォルト引数は呼び出しごとに初期化されない

Posted at

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ならば空のリストで初期化、みたいなコードにしてあげれば良さそう。

3
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?