世界を動かす技術を、日本語で。

PEP 810 – 明示的遅延インポート

概要

  • Python 3.15 で導入予定の lazy import 構文提案
  • lazy import は、指定したモジュールの 読み込み・実行を初回アクセス時まで遅延
  • 起動時間短縮・メモリ節約 など、特にCLIツールや大規模アプリで有効
  • 従来のimport文との 後方互換性を完全維持
  • 明示的・局所的・安全に インクリメンタル導入 可能

PEP: 明示的な lazy import 構文の導入

  • lazy import 構文の新設による、遅延インポート機能の明文化
    • 例: lazy import json
    • 例: lazy from json import dumps
  • 通常のimport文 は即時インポート、 lazy import は初回利用時まで遅延
  • 不要な依存モジュールの即時ロード回避 による、アプリ起動の高速化
  • メモリ使用量の削減 と、実際に必要なコードパスのみ依存解決
  • コマンドラインツール・テストスイート・大規模依存アプリ での有用性
  • 既存のimport文は影響なし、lazy指定時のみ遅延動作
  • if TYPE_CHECKING: など型チェック用インポートの最適化
  • 局所的・明示的・逐次的な導入 を重視した設計
  • 依存グラフの可視性維持、import分散による可読性低下を回避
  • LazyLoader やサードパーティ実装との差別化
  • lazy_modules による、旧バージョンとの互換サポート
  • dictの改変やグローバルフラグ方式は不採用、安全性と最適化重視

動機と利点

  • モジュール先頭でのimport集中 が従来の慣例
    • これにより 依存関係の明示性一度きりの評価 を実現
  • 即時インポートによる無駄な依存ロード が、起動遅延・メモリ増大の原因
  • 関数内import による手動遅延手法は、保守性・依存追跡性に課題
  • 標準ライブラリ分析 でも、約17%が遅延目的で関数内importを採用
  • lazy import構文 により、 依存の一元管理・明示的遅延 を両立
  • CLIツールの起動時間50-70%短縮、メモリ30-40%削減の実績
  • 型アノテーション用import のランタイム負荷ゼロ化
  • 大規模アプリの未使用サブシステムのメモリ節約

設計上の根拠と意思決定

  • 明示的なlazyキーワード の導入で意図を明確化
  • import文単位での局所的適用、依存先へのカスケードはなし
  • 辞書(dict)の改変回避、プロキシオブジェクト方式で安全性確保
  • 通常importとの区別を容易化、後方互換性の維持
  • lazy_modules による段階的導入サポート
  • インクリメンタルな導入 で既存コードベースへの影響最小化
  • グローバルスイッチ による一括適用も選択肢として用意

他の設計判断

  • 遅延のスコープは完全にローカル (import文ごと)
  • グローバルなlazy import制御 も可能(大規模アプリ・テスト環境向け)
  • 辞書(dict)のサブクラス化やグローバルフラグ案は不採用
    • Pythonオブジェクトの根幹であるdictの最適化・安全性重視
  • プロキシ方式 で初回アクセス時に通常importへ置換
  • CLIツール開発者などが段階的に導入できる構成

このPEPは、 Pythonのimport機構に明示的な遅延インポート機能 を加えることで、 起動時間・メモリ効率・依存管理性 の向上を目指すものです。 既存コードとの互換性・導入のしやすさ・安全性 を重視した設計となっています。

Hackerたちの意見

いい感じの機能だね。説明もシンプルで、実際の使い方もあって、スコープも明確(グローバルのみ、かなりシンプルなキーワード)。気に入った!

うん、これはかなりクリーンなPEPだと思うよ。特にユーザースペースの視点から見てね。伝統的な文法の議論が終わった後、どうなるか楽しみだな。

PEP-690が却下された理由から学んでくれたらいいな。うちのコードベースのためにこれを作るのにかなりの時間を費やしたけど、うまくいった試しがないんだよね。

同意するよ。彼らは本当に宿題をやったし、エッジケースをリストアップして、実用的な妥協をし、やりすぎないように選んで、何度も何度も再構築して、実際の経験と比較している。特に、Pythonのように多様なコミュニティを持つ言語のバックボーン(インポートシステム)に手を加えるのは超危険な手術だから、本当に美しい仕事だと思う。感心してるよ。

誰か興味があれば、結構使いやすいレイジーインポートの仕組みをコンテキストマネージャーの形で実装したよ(auto_proxy_import/init)。これを結構使ってる。文法的には、特に変更のないインポート文をwithブロックでラップするだけだから、ツールもそのまま動くし、簡単に無効化したりデバッグ用に早めにインポートさせたりできる。主にフレームのf_builtinsをcextで入れ替えることで動いてるけど(importlibのフックよりもパワーが必要だから)、スレッドセーフな純粋なPython版の試みもしてるし、超バカなグローバルフック版もある。最初は懐疑的だったけど、今では大部分のコードをこれに移行したよ。意外と問題は少なくて(正直、いくつかのモジュールでインポート時の登録を忘れるくらい)、速度の向上は中毒性があるね。

明示的にオンオフできるから好きだな。Dockerベースのアプリでは、インポートの完全性を確認したいんだ。できればビルドとテストの時にね。実際、ほとんどの場合はレイジーローディングを完全に無効にすると思う。でも、もっと早く読み込めるCLIツールがあったら嬉しいな。ただ、Pythonには、例えばpandasがExcelライブラリをインストールしてないとエラーを出すパターンがあるから、それはそれでいいんだけど。将来的には、メンテナーたちはスタートアップ時間に悪影響を与えないからって、使わないライブラリをたくさん含める選択をするのかな?(pandasがデフォルトで3-4個のExcelパーサーを含めるみたいに、呼び出された時だけ読み込まれるから)。UXはずっと良くなるけど、レイジーローディングをオフにすると、コードの読み込みが遅くなるんだよね。

レイジーインポートは以前にも提案されたことがあって、最近では2022年に却下されたよね。もし記憶が正しければ、レイジーインポートはCinder、MetaのCPythonバージョンでサポートされていて、PEPはCinderに関わっていた人たちによって推進されたんだ。前回は、これがオプトインかオプトアウトか、どのレベルでやるべきか、CPython自体のビルドフラグにするべきかなどの質問が多く出たよ。リンク先の投稿によると、ステアリングカウンシルは、2つの異なる「モード」のインポートを持つことによる複雑さから最終的に却下したみたい。今回の提案が成功することを願ってる。この機能を使いたいな。

うーん。バージョンインポートをサポートしてほしいな。例えば、import torch==2.6.0+cu124import numpy>=1.2.6 みたいに。Pythonライブラリの複数のバージョンを同時にインストールできるようにして、condaやvirtualenv、docker、bazelのごちゃごちゃを終わらせてほしい。

特にオプトインで、さまざまな粒度のレベルがあって、グローバルなオフスイッチもあるからね。制約を考慮した上で、よく構築された仕様だと思う。

これ大好き。私のCLIツール(https://llm.datasette.io/)はプラグインをサポートしてるんだけど、「llm --help」みたいなコマンドでも起動がすごく遅いって文句があったんだ。人気のプラグインがベースレベルでpytorchをインポートすることがあって、全体の起動が重いインポートでブロックされてたみたい。プラグインの作者向けドキュメントに、関数内でのレイジーローディングを提案するノートを追加したけど(https://llm.datasette.io/en/stable/plugins/advanced-model-pl...)、これがPythonのコア機能としてあったら本当にいいな。

これを今日から自分のツールで実装できるよ: https://news.ycombinator.com/item?id=45467489 これはプロセス全体にグローバルだから、例えばNumpyをこの方法でレイジーにインポートすると、すべてのサブモジュールのインポートもレイジーになる。つまり、Numpyの大部分は必要ない場合、全くインポートされないかもしれないけど、個々のモジュールのインポートのための待機時間がランタイム全体で予測不可能に分散されるかもしれない。編集:さらに実験した結果、ソースが import foo.bar.baz のようなことをすると、foofoo.bar はまだ早めにロードされて、foo.bar.baz 自体だけが遅延されるみたい。これがPEPが「ほとんど」と言っていたことの一部かもしれない。でも、私の実装を改善してそれを修正することもできるかもしれない。

コマンドラインを解析して、インポートをせずに「--help」みたいなことをする。必要だとわかった時だけインポートを行うべきだし、簡単なコマンドラインオプションが処理された後にまだやることがあるときだけインポートすればいい。

Hacker Newsで議論の続きを見る