LoginSignup
1
2

More than 1 year has passed since last update.

pythonで空のリストをデフォルト引数にしようと def func(l=list()) と書いてはいけない

Posted at

関数書くときに

  • 引数を受け取らなかった → 結果をlistにして返す
  • 引数にlistを受け取った → 続きに書き足して返す

みたいな動作を書きたいことがあると思います

失敗した書き方

そこで、以下のように書いてみました

上手くいっているように見える例1
def func1(n, l=list()):
    for k in range(n):
        l.append(k)
    return l

func1(3)  # [0, 1, 2] が返ってきます
上手くいっているように見える例2
def func1(n, l=list()):
    for k in range(n):
        l.append(k)
    return l

l1 = func1(3)
l2 = func1(3, l1)
l2  # [0, 1, 2, 0, 1, 2] が返ってきます

どうやら期待通り動いてんね、という事で以下を実行するとおかしなことになります

失敗が分かる例
def func1(n, l=list()):
    for k in range(n):
        l.append(k)
    return l

l1 = func1(3)
l2 = func1(3, l1)  # l2は [0, 1, 2, 0, 1, 2] になっている

l3 = func1(2)
l4 = func1(2, l3)  # l4は [0, 1, 0, 1] になっている筈が…

l4  # [0, 1, 2, 0, 1, 2, 0, 1, 0, 1] が返ってきます

…どうやら何か失敗しているようです

動作を確認する

上記のl1~l4のオブジェクトIDを確認してみます

IDを確認
>>> print(id(l1), id(l2), id(l3), id(l4))
2093705104064 2093705104064 2093705104064 2093705104064

引数を受け取らなかった場合は新しく空のlistを作ってそこに書き足していくという動作を期待していたので

  • l1~l2が同じオブジェクト
  • l3~l4が同じオブジェクト
  • l1~l2とl3~l4は別のオブジェクト

というのを期待していたんですが全部同じIDになってます。これが意図しない動作の原因のようです。

考察

IDを見て考えた推測ですが、1行目の「def func1(n, l=list()):」が原因ではないかと思います。list()により作成されたlistオブジェクトが関数情報を保管するスコープに置かれていて、引数を省略したらその作成済みlistオブジェクト(上の例だとIDが2093705104064になっているlistオブジェクト)を引数として受け取るという事ではないでしょうか。インタプリタが関数をパースするときにlist()を普通に実行して格納しちゃってる感じかな?と思ってます。

対策

というわけでNoneのように判別がやりやすいやつをデフォルトに入れておいて、Noneだったら新しいlistを作るという書き方に変えればやりたかった動作が実現できます。

対策例
def func2(n, l=None):
    if l is None:
        l = list()

    for k in range(n):
        l.append(k)
    return l

l1 = func2(3)
l2 = func2(3, l1)

l3 = func2(2)
l4 = func2(2, l3)

print(l4)
print(id(l1), id(l2), id(l3), id(l4))

関数が上から下に順番に実行されるものと考えているとハマりやすい罠ではないかと思います。ご注意ください。

試行環境

Windows10 + miniconda
python 3.8.8

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2