テクノロジー · 実用ガイド
Python の「dictionary changed size during iteration」—コピーやロックの前に変更元を証明する
同一ループ、並行ライター、再入コールバック、ランタイム所有レジストリを分け、検証可能な修正を選ぶ。

この RuntimeError は、走査中にマッピングのキー集合が変わった境界シグナルです。同じループ、別スレッド、コールバック、Python や依存関係のレジストリのどれが変更したかまでは示しません。
トレースバックを保存し、所有者と変更経路を証明してから、スナップショット、二段階更新、共有同期、上流修正を選び、失敗したインターリーブで不変条件を検証します。
1. トレースバックと対象マッピングを固定する
完全なトレースバック、Python とパッケージの版、スレッド/タスク名、マッピングの安定した識別子を保存し、自分のループ、deepcopy、weakref、sys.modules、executor、依存関係のどこで失敗したか確認します。
d、keys、values、items のどれを走査したか、キーの追加・削除か既存値の更新かを記録します。値更新中のコールバックがキーを変える場合もあります。
2. 変更元を一種類ずつ再現する
コールバックと背景ライターを止めても失敗するなら、ループ内や呼び出し先の delete、pop、clear、欠損キーへの setdefault、挿入を調べます。負荷時だけなら全書き込み経路を計測します。
deepcopy や sorted のフレームは変更元とは限りません。弱参照コールバック、終了処理、比較、ログ、シグナル、デストラクタが再入して変更できます。
3. スナップショットか二段階更新を意図的に選ぶ
時点ビューでよい場合は list(d)、list(d.items())、tuple(d)、d.copy() を意味に合わせて使います。浅いスナップショットはキー走査を安定させるだけです。
走査で変更対象を決めるなら、変化しないビューから判断を集め、後で適用します。破壊的消費が目的なら明示的な popitem ループが意図を表します。
4. 並行性と再入の所有権を実体化する
複数スレッドなら、一貫性が必要な読み取りと全構造書き込みを同じ Lock/RLock に通すか、単一スレッド所有とキュー通信にします。リーダーだけのロックはライターを同期しません。
未知のコールバックを任意ロック保持中に呼ばず、所有境界内で有限スナップショットを取り、解放後に呼ぶか、副作用を次段階へ送ります。
5. ランタイム所有レジストリは依存関係事故として扱う
sys.modules、weakref、ProcessPoolExecutor、プラグイン管理などの内部なら、対応中の最新版で再現し、課題と変更履歴を確認し、最小再現と修正版を保存します。
旧インターリーブを強制するストレス試験で、キー欠落・重複、順序仮定、コールバック一回性、意図した陳腐化を検証します。例外が消えただけでは不十分です。
