Python の class でフィールドの初期値をリストにする

クラスのフィールドにリストを初期値として設定する場合、ミュータブル(変更可能)なオブジェクトの扱いに注意が必要。

間違いやすいパターン

デフォルト引数にリストを直接指定すると、すべてのインスタンスで同じリストオブジェクトが共有されてしまう。

class Task:
    def __init__(self, name, tags=[]):  # 危険なパターン
        self.name = name
        self.tags = tags

task1 = Task("買い物")
task1.tags.append("緊急")

task2 = Task("掃除")
print(task2.tags)  # ['緊急'] ← task1 の変更が影響している

これは Python のデフォルト引数が関数定義時に一度だけ評価されるためである。

正しい方法:None を使う

デフォルト値に None を指定し、__init__ 内で新しいリストを生成する。

class Task:
    def __init__(self, name, tags=None):
        self.name = name
        self.tags = tags if tags is not None else []

task1 = Task("買い物")
task1.tags.append("緊急")

task2 = Task("掃除")
print(task2.tags)  # [] ← 正しく独立している

この方法なら、各インスタンスが独自のリストを持つ。

dataclass を使う方法

Python 3.7 以降では dataclass を使うとより簡潔に書ける。field(default_factory=list) を使用する。

from dataclasses import dataclass, field

@dataclass
class Task:
    name: str
    tags: list = field(default_factory=list)

task1 = Task("買い物")
task1.tags.append("緊急")

task2 = Task("掃除")
print(task2.tags)  # []

default_factory には呼び出し可能オブジェクトを渡す。インスタンス生成のたびにこれが呼ばれ、新しいリストが作られる。

まとめ

従来の __init__ 方式

デフォルト引数に None を指定し、メソッド内で if tags is None のチェックを行う。シンプルなクラスや Python 3.6 以前で使う。

dataclass 方式

field(default_factory=list) を使う。型ヒントと組み合わせてコードが簡潔になり、__init____repr__ が自動生成される。

どちらの方法でも、ミュータブルなデフォルト値を直接指定しないことが重要。