解析用の.pyから関数だけを使いたいのに、importした瞬間に計算や画面表示まで始まることがある。単体実行時だけ動かしたい処理は、if __name__ == “__main__”:の内側から呼び出す。関数の定義と、入力・計算・出力を実行する入口を分ければ、同じファイルをスクリプトとしても部品としても使いやすくなる。
importはファイルのトップレベルを実行する
Pythonのimportは、ファイルを単なるテキストとして登録するだけの操作ではない。初めて読み込むとき、トップレベルに書かれた文を実行してモジュールを初期化する。その場所にprint、input、ファイル保存、大きな計算があれば、関数を一つ使いたいだけでも動いてしまう。
def文もトップレベルで実行されるが、その段階では通常、関数本体は呼ばれない。この違いを使い、再利用したい処理を関数として定義する。なお、デフォルト引数やデコレーターは定義時に評価されるので、それらに重い処理を書けば完全に副作用がなくなるとは限らない。
関数と実行入口を分ける
次のanalysis_tools.pyでは、平均値を求める関数を定義し、表示する操作をmainにまとめた。最後の条件が成立したときだけmain()を呼ぶ。入力が空の場合は、意味のある平均値が作れないのでValueErrorにしている。
analysis_tools.py
def mean(values):
if not values:
raise ValueError("values must not be empty")
return sum(values) / len(values)
def main():
print("mean:", mean([1, 2, 3]))
if __name__ == "__main__":
main()
このファイルを単体で実行すると__name__は__main__になる。別のファイルからimport analysis_toolsした場合には、通常はanalysis_toolsというモジュール名になるため、条件の内側は実行されない。mainという関数名自体に特別な自動実行の意味はない。
別ファイルから呼び出す
同じフォルダーに次のuse_tools.pyを置けば、平均計算だけを再利用できる。関数の呼び出しに必要な値を引数へ渡し、戻り値を受け取っているので、analysis_tools.pyがどのデータを単体実行に使っていたかには依存しない。
use_tools.py
from analysis_tools import mean
result = mean([2, 4, 6])
print("reused:", result)
assert result == 4.0
実行結果
mean: 2.0
import output: ''
reused: 4.0
上の結果は、単体実行、importだけの実行、利用側ファイルの実行をそれぞれ新しいPythonで確かめたものだ。importの出力が空文字列になっているため、単体実行用のprintが読み込み時に動かないことを確認できる。
ガードの外に何を置くか
定数、クラス、関数など、モジュールを使うために必要な定義はガードの外へ置く。一方、試しに計算するデータの準備、グラフを表示する処理、ファイルへ結果を書き込む操作はmainなどの実行用関数へ移すとよい。何でも条件の内側へ入れると、importした側から必要な定義まで見えなくなる。
特にinputで入力待ちになる処理や、読み込み時に作業ディレクトリを変える処理は、部品として使う側へ思わぬ影響を与える。関数へ値やパスを渡す形へ変えることで、実行条件を呼び出し側に集められる。グローバル変数へ依存する関数も、必要な値を引数にできないか見直したい。
同じプロセスのimportだけで確認しない
通常、同じモジュールを繰り返しimportしても、すでに読み込まれたものが再利用される。そのため「二回目には処理が走らないから大丈夫」と判断すると、初回だけ起こる副作用を見落とす。確認するときは、新しいプロセスから一度だけimportしてみる。
ガードはトップレベルのすべてを無効化する仕組みではない。例えば条件より前に置いたprintは、import時にもそのまま実行される。必要なのは決まり文句を末尾に追加することではなく、単体実行で行いたい操作を実際に分離することである。
ファイルが増えてパッケージへ整理したくなったら、__main__.pyを実行入口にし、python -mで起動する構成へ進められる。まずは一つのファイルでも、計算関数が戻り値を返し、mainが表示を担当する形にすると、再利用と小さな動作確認の両方が容易になる。
動作確認と参考資料
掲載例はLinux・CPython 3.12.14で動作確認した。OS固有のコマンドや環境ごとに変わるパスは、本文中の条件を確認して使ってほしい。
