madokaのブログ

勉強したことのoutput先として使ってます。内容はpythonがらみが多いかもです。

pythonの参照型をデフォルト引数にすることとは

javaをメインにお仕事してたので、なかなかお目にかかることのなかったデフォルト引数 (javaにはない) 。最近pythonを書いていて、たまたまデフォルト引数にlistをいれてみようかなと思って書いてみたら、intelliJさんに黄色くされたので気になって調べてみることに。

デフォルト引数について思うこと

そもそもデフォルト引数とは引数の初期値。

javaだと引数減らした同じ名前のmethodをもう一つ作ってoverloadというかたちをとったり、場合によってはnull判定をしてnullだったらこれ入れるみたいなことをしてたのが、デフォルト引数で全て片付くのでコードがすっきりするので存在に感謝している。

デフォルト引数は使い回す。

print_sampleの引数としてもらったsのidを出力するというコードを書きました。

def print_sample(s=1):
    print(id(s))


if __name__ == '__main__':
    print_sample() #1
    print_sample(2) #2
    print_sample() #3

出力

4353093824 #1
4353093856 #2
4353093824 #3

出力を見ていただけるとわかるようにデフォルト引数を使うことになった#1,#3でidが同じことがわかります。このことから、デフォルト引数が毎度生成されているわけではないということが言えそうです。

デフォルト引数にlistを入れるということは。。。

デフォルト引数にlistをいれるなんて、なんて恐ろしいことなんだと気がつきました。

さきほどデフォルト引数が毎度生成されていないということがわかったので、つまるはなし参照型のものをデフォルト引数にしてしまうとlistに要素を追加するなどのデータが書き換えがあった場合に、次にデフォルト引数を使うときには中身が想定のもとは異なってしまうということです。

またこのmethodを実際に使うことを考えると、以前にどんな呼び出され方をしたのか意識して使わなければいけないということです。ああ、なんておそろしいんだ。。

恐ろしさを実感するコードが以下になります。

def print_list(n, l=[]):
    print(l)
    l.append(n)


if __name__ == '__main__':
    print_list(2)
    print_list(3)
    print_list(0)

出力

[]
[2]
[2, 3]

デフォルト引数のlistに要素が増えていることがわかります。

まとめ

intelliJのwarningは聞いておくものだなとしみじみと感じました。これからはもうちょっとintelliJさんの声に耳を傾けようと思います。