【Python】パッケージの絶対インポートがよくわからなかったので

FIXME: 要修正

FastAPIのプロジェクトを作成した際に、パッケージのルートはどこになるんですか?

FastAPIプロジェクトを作成する際、特定の規定されたディレクトリ構造が存在するわけではありません。しかし、一般的なプロジェクト構造やベストプラクティスに基づくと、プロジェクトのルートディレクトリは通常、プロジェクト名と同じディレクトリになります。

例えば、myfastapiという名前のFastAPIプロジェクトを作成する場合、以下のようなディレクトリ構造が考えられます:

myfastapi/
│
├── app/
│   ├── api/
│   │   ├── endpoint1.py
│   │   ├── endpoint2.py
│   │   └── ...
│   ├── models/
│   ├── schemas/
│   ├── services/
│   ├── main.py  # or app.py or run.py
│   └── ...
│
├── tests/
├── .gitignore
├── pyproject.toml  # または requirements.txt
└── README.md

この場合、myfastapi/がプロジェクトのルートディレクトリになります。そして、app/ディレクトリがアプリケーションの主要なコードを含む場所となります。

しかし、FastAPI自体はこのような構造を強制するものではなく、この構造は一例として示されています。プロジェクトの要件や好みに応じてディレクトリ構造を適切に調整することが可能です。

例えば、appをパッケージにして、apiをサブパッケージにした場合、endpoint1.pyに何かmodelsのモジュールをインポートしようとすると絶対インポートはどうなりますか?

appをパッケージとして、その中にapiをサブパッケージとする場合、そしてmodelsに何らかのモデル定義があると仮定すると、ディレクトリ構造は以下のようになります。

myfastapi/
│
├── app/
│   ├── api/
│   │   ├── endpoint1.py
│   │   └── ...
│   ├── models/
│   │   ├── user.py  # 例として
│   │   └── ...
│   └── ...
│
└── ...

この場合、endpoint1.pyからuser.py内のモデルを絶対インポートするには、以下のようにします。

from app.models import user

ここで、userapp.modelsパッケージ内のuser.pyモジュールを指します。もしuser.py内にUserという名前のクラスが定義されている場合、以下のように具体的なクラスをインポートすることもできます。

from app.models.user import User

この方法で、endpoint1.py内でmodelsパッケージの中のuser.pyモジュールやその中のUserクラスを使用することができます。

なぜappがルートになるのですか?どうやって判別しているのですか?

実際には、app がルートになっているわけではありません。プロジェクトディレクトリ myfastapi がルートになります。ただし、Pythonのインポート機構を理解すると、なぜ app からのインポートが機能するのかが明らかになります。

Pythonがモジュールやパッケージをインポートするとき、それを見つけるためのいくつかの場所が sys.path というリストに列挙されています。デフォルトで、このリストには標準ライブラリやインストールされたサードパーティのライブラリの場所が含まれています。また、実行されているスクリプトのあるディレクトリも自動的にこのリストに追加されます。

たとえば、myfastapi ディレクトリで FastAPI アプリケーションを実行すると、そのディレクトリが sys.path に追加されます。これにより、Pythonは app やその他のサブディレクトリやモジュールを見つけることができます。したがって、app から始まるインポートが可能になります。

しかし、例えば app ディレクトリ内で直接 Python スクリプトを実行すると、その場合 app ディレクトリ自体が sys.path に追加されるため、インポートの際には app を指定せずに他のサブモジュールやサブパッケージをインポートする必要があります。

この振る舞いを確実にしたい場合は、プロジェクトルートに __init__.py を置いて、ルートを明示的なパッケージとして扱うという手も考えられますが、通常の FastAPI プロジェクトではこのような構造は推奨されません。

要するに、Pythonのインポートの動作は、実行コンテキストと sys.path の現在の内容に強く依存していると言えます。

なるほど、じゃあmain.pyがappにある場合は、app ディレクトリ自体が sys.path に追加されるから、絶対インポートの先頭がappではなくなりますか?

はい、正確にはその通りです。

もし main.pyapp ディレクトリの中に存在していて、その main.py を実行する場合、app ディレクトリが sys.path に自動的に追加されます。したがって、app ディレクトリを起点としてインポートが行われます。

具体的なディレクトリ構造が以下のような場合:

myfastapi/
│
├── app/
│   ├── api/
│   │   ├── endpoint1.py
│   │   └── ...
│   ├── models/
│   │   └── ...
│   ├── main.py
│   └── ...
│
└── ...

この状況で、app/main.py を実行すると、インポート文は以下のようになります:

from api import endpoint1  # 例
from models import user  # 仮にuser.pyがmodelsの中に存在する場合

この場合、app をインポートのパスに含める必要はありません。なぜなら、現在の実行ディレクトリ(app ディレクトリ)が sys.path に自動的に追加されるからです。

> sys.path に自動的に追加される これはFastAPIの機能ですか?

いいえ、これはFastAPIの機能ではありません。sys.path への現在の実行ディレクトリの追加はPython自体の動作によるものです。

Pythonを起動すると、Pythonは実行されているスクリプトのあるディレクトリを自動的に sys.path の先頭に追加します。この動作は、スクリプトがどのフレームワークやライブラリを使っているかに関係なく、Python自体のデフォルトの動作として行われます。

したがって、あるディレクトリでPythonスクリプトを実行すると、そのディレクトリは自動的にモジュールの検索パスに含まれることになります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です