Pythonのデフォルト引数での注意点

この記事では、Pythonのデフォルト引数の注意点について記載します。

デフォルト引数とは

Pythonでは関数(やメソッド)を定義する際に、引数のデフォルト値を設定することができます。デフォルト引数を設定すると、関数呼び出し時に引数が省略した場合に、設定したデフォルト値が使用されます。

def foo(text='おはよう'):
    print(text)

foo()
# おはよう
foo(text='こんにちは')
# こんにちは

上記の関数ではデフォルト引数として、textにおはようを設定しました。そのため、関数呼び出し時に引数を省略した場合は、デフォルト値が使われ、引数を明示的に指定した場合は、指定した引数の値が使用されます。

デフォルト引数は、デフォルトのパラメータを設定しておく場合など便利に使えます。

デフォルト引数の注意点

このように便利なデフォルト引数ですが、大きな注意点があります。
それは、デフォルト引数にリストや辞書などのミュータブルなオブジェクトを指定してはいけないということです。

なぜなら、Pythonのデフォルト引数の値は関数呼び出しのたびに初期化されず、関数定義にのみ評価されるからです。そのため、ミュータブルなオブジェクトをデフォルト引数に指定すると何度も同じオブジェクトが呼び出されます。
デフォルト引数で呼び出されたオブジェクトに対して変更を加えると、次回関数を呼び出された際に、変更が加えらたオブジェクトが呼びだされることになります。

デフォルト引数にミュータブルなオブジェクトを指定した際の挙動

以下にデフォルト引数にリストを指定した関数の例を示します。

def foo(text_list=[], text='おはよう'):
    text_list.append(text)
    print(text_list)

foo()
# ['おはよう']
foo()
# ['おはよう', 'おはよう']
foo()
# ['おはよう', 'おはよう', 'おはよう']
foo(text_list=[])
# ['おはよう']
foo()
# ['おはよう', 'おはよう', 'おはよう', 'おはよう']

上記の関数は、引数を指定しないと、関数が呼ばれるたびに同じオブジェクトをデフォルト引数として利用しますので、呼び出すたびに同じリストに値をappendしてしまうので、出力結果が変化します。

注意点への対策

では、どうすれば良いかというとデフォルト引数にミュータブルなオブジェクトを指定するのではなく、Noneを指定して関数内でNoneの場合、空のミュータブルなオブジェクトを指定します。

def foo(text_list=None, text='おはよう'):
    text_list = text_list or [] # Noneの場合に空のリストを指定
    text_list.append(text)
    print(text_list)

foo()
# ['おはよう']
foo()
# ['おはよう']
foo()
# ['おはよう']
foo(text_list=[])
# ['おはよう']
foo()
# ['おはよう']

上記のように記述すると、引数が指定されていなくても関数呼び出しの際に、オブジェクト(上記の関数であれば空のリスト)が初期化されます。

終わりに

今回はPythonのデフォルト引数での注意点とその対策をまとめました。
一度はまると上記の注意点は忘れないのですが、この記事を読んだ方がこの仕様にはまらずにクリアしていただけると嬉しいです。

PythonPython

Posted by mako