Python のデフォルト引数に [] を書いてはいけない理由: デフォルト値は def の実行時に一度だけ作られる

def f(a, L=[]) を何度も呼ぶとリストが増え続けます。Python 公式ドキュメントが説明するデフォルト値の評価タイミング、__defaults__、None を使う書き方、dataclass の default_factory を例で整理しました。

開発Python

次の関数を 3 回呼ぶと、何が出力されるでしょうか。

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

呼び出しのたびに新しいリストができると考えると、[1]、[2]、[3] を期待します。実際の出力は [1]、[1, 2]、[1, 2, 3] です。この例は Python 公式チュートリアルに「重要な警告」としてそのまま載っています。バグではなく、言語仕様どおりの動作です。

この記事は 2026 年 10 月 10 日に確認した Python 3.15.0 のドキュメントを基準にしています。ここで扱うデフォルト値の評価規則は Python 3 を通して変わっていません。バージョンによって違う点は dataclass の節に分けて書きました。

デフォルト値は def 文の実行時に評価される

言語リファレンスの関数定義の節は次のように定めています。デフォルト引数の値は関数定義が実行されるときに左から右へ評価され、そうして一度計算された値が毎回の呼び出しで使われます。

def は宣言ではなく、実行される文です。モジュールを import したときやスクリプトがその行に達したとき、Python は関数オブジェクトを作り、その時点で = の右側の式を計算して結果を関数オブジェクトに持たせます。呼び出し時に式が再計算されることはなく、保持しているオブジェクトがそのまま引数に結び付けられます。

そのため L=[] の空リストは関数ごとに 1 つしかありません。引数を省略した呼び出しはすべてこのリストを受け取り、append で加えた内容は次の呼び出しでもそのまま残っています。

チュートリアルの別の例も同じ規則を示しています。

i = 5

def f(arg=i):
    print(arg)

i = 6
f()   # 5

def が実行された時点で i は 5 だったので、デフォルト値は 5 です。あとで i を 6 に変えてもデフォルト値は変わりません。

関数オブジェクトで確かめる

計算済みのデフォルト値は、関数オブジェクトの __defaults__ 属性にタプルとして入っています。データモデルのドキュメントはこの属性を「デフォルト値を持つ引数のデフォルト値からなるタプル、デフォルト値を持つ引数がなければ None」と説明しています。キーワード専用引数のデフォルト値は別に __kwdefaults__ 辞書に入ります。

上の f(a, L=[]) を 3 回呼んだあとで f.__defaults__ を見れば、([1, 2, 3],) が入っているはずです。呼び出しの間でデフォルト値が「覚えている」場所はここです。デバッグ中に関数が隠れた状態を持っているように見えたら、この属性を表示してみるのがいちばん手早い確認方法です。

問題になるのはどんな値か

デフォルト値が共有されること自体は問題ではありません。共有されたオブジェクトを関数が変更したときに問題が起きます。公式 FAQ は、数値、文字列、タプル、None のようなイミュータブルなオブジェクトは変更されないので安全で、辞書、リスト、クラスインスタンスのようなミュータブルなオブジェクトが混乱のもとになると説明しています。チュートリアルの警告も、リストや辞書とあわせて「ほとんどのクラスのインスタンス」を挙げています。

よく見かける形は次のとおりです。

  • def add(item, items=[]) のように、受け取ったコレクションに append や update をする関数
  • def __init__(self, tags=[]) で self.tags = tags と保存するコンストラクタ。引数なしで作ったインスタンスがすべて同じリストを共有します。
  • def log(msg, when=datetime.now()) のように、呼び出し時刻をデフォルト値にしようとする関数。時刻は def が実行された瞬間に一度だけ計算されるので、その後の呼び出しはすべて同じ時刻を記録します。

最後の例は値がミュータブルだからではなく、評価のタイミングによる問題です。どちらも「デフォルト値の式は関数定義時に一度だけ実行される」という同じ規則から来ています。

None で受けて関数の中で作る

リファレンス、チュートリアル、FAQ が勧める方法は同じです。デフォルト値を None にし、関数本体で None かどうかを確かめて新しいオブジェクトを作ります。

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

これで [] は呼び出しのたびに実行されるので、引数を省略した呼び出しは毎回新しいリストを受け取ります。時刻の例も同じで、when=None で受けてから本体で datetime.now() を呼びます。

判定は if L is None と書きます。短く L = L or [] と書くコードもありますが、この式は None だけでなく偽と評価されるすべての値を置き換えます。呼び出し側が結果を受け取るために空のリストを渡しても、関数はそのリストではなく新しいリストに値を入れます。渡したリストは空のまま残ります。

None 自体が意味のある引数で「省略された」と区別したい場合は、モジュールレベルに _MISSING = object() のような専用オブジェクトを作ってデフォルト値にし、is で比較できます。このオブジェクトはほかの場所では作れないので、呼び出し側がうっかり渡すことはありません。

わざと共有する場合もある

FAQ は、この動作が役に立つこともあると付け加えています。時間のかかる関数の結果を覚えておくメモ化の例です。

def expensive(arg1, arg2, *, _cache={}):
    if (arg1, arg2) in _cache:
        return _cache[(arg1, arg2)]
    result = ...  # 時間のかかる計算
    _cache[(arg1, arg2)] = result
    return result

* の後ろに置いてキーワードでしか渡せないようにし、名前の先頭のアンダースコアで内部用であることを示しています。FAQ は辞書を入れたグローバル変数を使ってもよく、好みの問題だとしています。ただ、意図を知らない人には先ほどのバグとまったく同じに見えるので、この書き方をするならコメントを残しておくのがよいでしょう。メモ化が目的なら、標準ライブラリの functools.cache(3.9 から)や functools.lru_cache デコレータを使う方法もあります。

dataclass は一部を防いでくれる

@dataclass では、フィールドのデフォルト値が生成される __init__ のデフォルト引数になるため、同じ共有が起こりえます。dataclasses のドキュメントによると、デコレータはハッシュ不可能なデフォルト値を見つけると ValueError を送出します。ハッシュ不可能ならミュータブルだろう、とみなす方式です。

from dataclasses import dataclass, field

@dataclass
class D:
    x: list = []                          # ValueError

@dataclass
class D:
    x: list = field(default_factory=list) # インスタンスごとに新しいリスト

このチェックは Python 3.11 で変わりました。3.10 までは list、dict、set 型だけを拒否し、3.11 からはハッシュ不可能なオブジェクトをすべて拒否します。ドキュメント自身も部分的な解決策だと書いています。ハッシュ可能なミュータブルオブジェクトは通ってしまうので、ミュータブルなデフォルト値が必要なフィールドには、引数なしの呼び出し可能オブジェクトを default_factory に渡すのが確実です。

普通の関数やクラスにはこのようなチェックはありません。def f(L=[]) は何の警告もなく定義されるので、コードレビューやリンターで見つける必要があります。

まとめ

  • デフォルト値の式は def の実行時に一度だけ評価され、そのオブジェクトがすべての呼び出しで再利用されます。
  • 関数がデフォルト値のオブジェクトを変更すると、その変更は次の呼び出しに持ち越されます。リスト、辞書、ほとんどのクラスインスタンスが該当します。
  • デフォルト値は None にして、本体で if x is None: と判定して新しいオブジェクトを作ります。
  • dataclass では field(default_factory=...) を使います。

参考資料

確認日: 2026 年 10 月 10 日(Python 3.15.0 ドキュメント)

ブログへ