到達点は、複数ファイルへ分けても実行手順を再現できることです。前提は第03回です。
importはファイルの貼り付けではない
一つのPythonファイルをモジュールとして扱えます。import時にはモジュールのトップレベルの処理が実行されます。読み込みだけで入力待ちやファイル更新が始まると、テストや再利用が困難になります。実行の入口はmain関数へ集めましょう。
同じフォルダにまずfees.pyを作ります。
def shipping(amount: int) -> int:
if amount < 0:
raise ValueError("購入額は0以上です")
return 0 if amount >= 5000 else 500
次にapp.pyを作ります。型注釈の意味は第10回で詳しく扱います。
from fees import shipping
def main():
print(shipping(4999))
print(shipping(5000))
if __name__ == "__main__":
main()
python app.py で500と0が出ます。python -c "import app" では表示しません。直接実行された場合だけmainを呼ぶ条件を付けたためです。
名前と場所も設計の一部
パッケージは関連モジュールをまとめる単位です。一般的な小規模構成ではディレクトリと __init__.py を用意し、python -m package.module で起動します。実行場所によってたまたま動くimportを増やすより、プロジェクトの入口を固定します。
ファイル名をjson.pyやtyping.pyにすると、標準ライブラリを自分のファイルで隠す場合があります。「ライブラリに存在するはずの関数がない」ときは、名前の衝突を確認します。循環importが起きたら、共通の型やルールを独立したモジュールへ分けられないか考えます。
外部依存は仮想環境へ導入します。アプリの再現には実際に使った版と推移的依存の情報を残し、ライブラリ配布では対応範囲を考えます。pip freeze は環境の一覧を作れますが、不要なパッケージまで含む点に注意します。pyproject.tomlによる配布は第19回で扱います。
練習と解答
練習:fees.pyの末尾へprintを足し、app.pyを実行すると何が起きるか調べます。
解答:import時にprintが実行されます。必要な初期化と、単なる実行用デモを区別し、後者はmainガードの中へ移します。importを何度書いても、通常はキャッシュされた同じモジュールが使われる点も確認しましょう。
公式資料
モジュールとパッケージ管理ガイドが参照先です。
Python全20回の目次 | 前の回 | 次の回