ソースを複数ディレクトリに分けた時のお作法
複数のディレクトリから import するときのお作法 by Gemini
Pythonでプロジェクトが大きくなり、ソースファイルを複数のディレクトリ(パッケージ)に分割する際、スムーズにインポートを行うための「標準的な作法」がいくつかあります。
ぐちゃぐちゃになりがちなパスの問題をスッキリさせるためのポイントを整理しました。
1. 推奨されるディレクトリ構造
まず、現代的なPythonプロジェクトで最も推奨されるのは 「srcレイアウト」 です。ソースコードを src フォルダに隔離することで、意図しないインポートミスを防げます。
my_project/
├── pyproject.toml # 設定ファイル
├── src/ # ソースコードをここに入れる
│ └── my_app/ # パッケージ名
│ ├── __init__.py
│ ├── main.py # 実行エントリーポイント
│ ├── module_a.py
│ └── utils/ # サブパッケージ
│ ├── __init__.py
│ └── helper.py
└── tests/ # テストコード
2. インポートの基本ルール
① 絶対インポート (Absolute Import) を使う
PEP 8(Pythonのスタイルガイド)では、絶対インポートが強く推奨されています。プロジェクトのルート(パッケージのトップレベル)からのフルパスで記述する方法です。
- メリット: どこから呼び出しているか一目でわかり、階層が変わっても壊れにくい。
- 書き方:
# src/my_app/main.py の中で記述する場合
from my_app.utils import helper
from my_app.module_a import MyClass
② 相対インポート (Relative Import) はパッケージ内のみ
同じパッケージ内の密接に関連したモジュールを呼ぶ際に . を使います。
- 書き方:
# src/my_app/module_a.py の中で utils.helper を呼ぶ場合
from .utils import helper
-
注意: スクリプトとして直接実行するファイル(
python main.py)の中で相対インポートを使うと、ImportErrorになります。
3. __init__.py の役割
ディレクトリを「パッケージ」として認識させるためのファイルです(Python 3.3以降は必須ではありませんが、置くのが一般的です)。
- 使い道: パッケージをインポートした際に、外部に公開したいクラスや関数をショートカットとして登録するのに便利です。
# my_app/utils/__init__.py
from .helper import fast_function # 外部から my_app.utils.fast_function で呼べるようになる
4. 実行時の「お作法」: python -m
複数のディレクトリに分けた場合、個別のファイルを python src/my_app/main.py のように直接実行すると、パスが通らずにエラーになることがよくあります。
これを解決する最もクリーンな方法は、プロジェクトのルート(my_project/)に移動して、モジュールとして実行することです。
# プロジェクトルートで実行
export PYTHONPATH=$PYTHONPATH:$(pwd)/src # パスを通す(または開発モードでインストール)
python -m my_app.main
[!TIP]
sys.path.append() は禁じ手?
初心者がやりがちなsys.path.append('../')などの動的なパス追加は、コードの可読性と保守性を下げるため、商用コードや大規模開発では避けるべき「禁じ手」に近い作法です。