「パーフェクトpython」読んでます。
1個1個のトピックについて非常に説明が詳しいので、python という言語を勉強するのに役立ちます。
今回は「5章 関数」を読んでいて「これは知らなかった!」ということがあったのでメモ。
pythonで関数を定義する時、引数にデフォルト引数を設定することができますよね。
def hoge(foo, bar, baz='xxx'): print '%s:%s:%s' % (foo, bar, baz)
この関数を呼び出す時に引数baz
を省略すると、デフォルト引数として設定した'xxx'
が使われます。
>>> hoge('aaa', 'bbb', 'ccc') aaa:bbb:ccc >>> hoge('aaa', 'bbb') aaa:bbb:xxx
で、注意するのはこういった例。
>>> def append_number(items=[]): ... items.append(1) ... return items ... >>> append_number() [1] >>> append_number() [1, 1] >>> append_number() [1, 1, 1]
上記のように、デフォルト引数に対して関数内で破壊的な処理をする と、
関数を呼び出すたびに処理の影響が蓄積されてしまいます。
デフォルト引数が作られるタイミングは「関数が呼び出される時」ではなく
「関数が作成された時」だから、ということのようですね。
このへんきちんと理解してコードを書かないと、思わぬ挙動をしてしまいそうです。
このことはけっこうネットでも取り上げられており、「引数のデフォルト値はImmutableなものにすること」などと書いてました。
書籍では、↑の関数を以下のように修正してましたが
>>> omit = object() >>> def append_number(items=omit): ... if items is omit: ... items = [] ... items.append(1) ... return items ... >>> append_number() [1] >>> append_number() [1]
個人的には紹介したQiitaの記事のように、None
を指定する方がしっくりきました。
クラスのメソッドとして定義するときにメソッドの外側に omit = object()
とか書きたくないので。
>>> def append_number(items=None): ... if items is None: ... items = [] ... items.append(1) ... return items ... >>> append_number() [1] >>> append_number() [1]