Python講座 / パッケージと安全な実行入口を作る
パッケージは関連するモジュールを階層でまとめる仕組みです。実行処理をmain()と明示的な入口へ置くと、別のコードからimportしただけでアプリケーションが自動実行されることを防げます。
通常パッケージの構成を作る
この入門例では__init__.pyを持つ通常パッケージを使います。名前空間パッケージにはこのファイルを省略できる構成もありますが、ここでは明示的な構造を確認します。コマンドはexpense_appの中ではなく、その親のprojectから実行します。
project/
expense_app/
__init__.py
math_tools.py
__main__.py
モジュール名とインストール可能なパッケージは、importでモジュールを読み込み、コードを分割するで扱う規則とつながります。
全ファイルを保存し、パッケージを実行する
expense_app/__init__.pyです。
"""Expense calculation package."""
expense_app/math_tools.pyです。
def total(amounts):
return sum(amounts)
expense_app/__main__.pyです。
from .math_tools import total
def main():
print(total([3500, 8000, 2500]))
if __name__ == "__main__":
main()
projectで次を実行します。
python -m expense_app
結果は14000です。-mはモジュール探索で対象を見つけ、パッケージの文脈を与えます。python expense_app/__main__.pyという直接のファイル実行は別で、この例の相対importに必要な文脈を同じようには与えません。
__name__で実行と再利用を分ける
実行対象には__main__という名前が与えられ、importしたモジュールにはパッケージ名・モジュール名が与えられます。if __name__ == "__main__"は、その違いでmain()を呼ぶか判断します。関数定義は最上位に置き、計算やファイル変更は呼び出しまで待たせます。
データファイルとモジュールは探し方が違います。相対的なデータパスは作業フォルダーに依存するため、配布するアプリでは明示的なパス引数やパッケージ内リソースを扱う方法が必要な場合があります。常にソースの場所から起動されるとは限りません。
importだけの動作を別に確認する
projectで実行します。
python -c "import expense_app.__main__; print('imported')"
表示はimportedだけです。合計も出る場合は、最上位でまだ実行している文を探します。アプリとしての実行と、安全に再利用するimportを別々にテストすると、出力確認だけでは見落とす問題を検出できます。
パッケージ名を変えたら、importパスと実行コマンドも合わせて変えます。ソースの整理と、配布用メタデータ・ビルド・導入検証は別の作業です。配布に必要な追加工程はパッケージ化で扱います。
パッケージ外側のプロジェクトルートから python -m package.module で実行するとパッケージの文脈が保たれます。相対importを使う内部ファイルを直接実行すると、親パッケージが不明なためImportErrorになることがあります。コードを変えず、実行場所とコマンドだけを変えて比較します。




