Pythonのデフォルト引数で[]とか{}を使うべきではないという話

シェアする

Pythonのメソッドのデフォルト引数に[]などを使うと、Pylintから次のように怒られました。

Dangerous default value [] as argument pylint(W0102)

とりあえず無視して適当に動かしたところ、案の定バグりました。

コードの例を示します。

これをベースに議論を進めます。

スポンサーリンク

何がまずいのか

まず、my_listをデフォルトのままMyClassインスタンスを生成します。すると、self.my_listにはデフォルト値である[]が入ります。その後、いろいろ処理をしているうちにself.my_listにappendしたりいろいろ変更を加えたりしたとしましょう。例えば、

次に、再びmy_list引数を与えずに別のMyClassインスタンスを生成したとしましょう。このインスタンスのself.my_listには何が入るでしょうか。実は、[‘A’]が入ります。別のインスタンスでのself.my_listに対する変更が引き継がれているのです。

なぜか

これは、Pythonのlistがmutableであることに起因します。

1つ目のインスタンスのデフォルト値は[]ですが、Pythonのlistはmutableですので、その参照が入ります。つまり、self.my_listとデフォルト値は同じ参照です。したがって、self.my_listに関する操作はデフォルト値である[]にも影響を及ぼします。2つ目のインスタンスを生成するときのデフォルト値はself.my_listと同じ参照なので、前のインスタンスにおける変更が残存するのです。

対処法

  1. copyを使う

    self.my_listとmy_listは入っている値は同じですが、その参照は異なります。
  2. Noneにしておいて、中で変える

    引数が与えられなかったときのみ、空のリスト[]を生成し、self.my_listに代入します。この[]は__init__が実行されるたびに生成されるので、複数のインスタンスで同じ参照になることはありません。引数を与えたときは、その参照に変更を及ぼします。

多くの場合で、copyを使う方法が素直かもしれません。

スポンサーリンク

シェアする

フォローする