Edited at

関数定義におけるmutableなデフォルト引数 ~python学習1.02~


デフォルト引数

mutableなデフォルト引数は気を付けないといけないことがあるらしい


デフォルト引数とは

関数定義にて、デフォルト値(初期値)を与えておくというものです。

デフォルト引数を用いることにより、関数で定義している引数よりも少ない引数で関数を呼び出すことができます。


qiita1.py

>>> def sample(self, number1=10, number2=20):

... print(self, number1, number2, sep=", ")

>>> sample("sampleString")
sampleString, 10, 20


ここで、デフォルト引数にあたる引数は第二引数と第三引数です。


デフォルト値は1度しか評価されない

気を付けなければならないのは、デフォルト値が1度しか評価されない点。

どういう場面で問題が起きるかというと


qiita2.py

>>> def f(name, L=[]):

... L.append(name)
... return L

>>> print(f('吉岡里帆'))
['吉岡里帆']

>>> print(f('吉高由里子'))
['吉岡里帆', '吉高由里子']

>>> print(f('のっち'))
['吉岡里帆', '吉高由里子', 'のっち']


第二引数のLは[]でデフォルト値が設定されているが、2回目のf()関数呼び出し時に吉岡里帆が残っている。

同様に、3回目のf()関数呼び出し時には吉岡里帆と吉高由里子が残っている。

公式ドキュメントにある警告文には、

"デフォルト値は 1 度だけしか評価されません。デフォルト値がリストや辞書のような変更可能なオブジェクトの時にはその影響がでます"

とありました。

これを解決するためには以下のようにコーディングするらしい。


qiita3.py

>>> def f(name, L=None):

... if L is None:
... L = []
... L.append(name)
... return L

>>> print(f('吉岡里帆'))
['吉岡里帆']

>>> print(f('吉高由里子'))
['吉高由里子']

>>> print(f('のっち'))
['のっち']


何が変わったかと言うと、デフォルト値が[]からNoneに代わりました。

[]をデフォルト値とすると、Lは変更可能なリストオブジェクトとなりますが、

Noneをデフォルト値とすると、Lは変更可能なオブジェクトではなく、Noneオブジェクトとなり、解決されています。

Noneとは、Javaでいうnullみたいなものらしいです(厳密にはちがう?)。


疑問

デフォルト値は1度のみの評価であるのに、なぜ2回目の呼び出し時にLはNoneオブジェクトを保持しているのか。

・L=[]で、仮引数であるLの参照がNoneオブジェクトから新規作成されたLのリストに移っている...?

・Pythonのスコープがわからないので疑問のまま...


開発環境

Windows10 64bit

Python 3.7.1

PyCharm 2018.3.2


参考資料

学習に用いているものとして、以下の2つがメインです。

詳細! Python 3 入門ノート

Python公式ドキュメント