こんにちは。今回はPythonの言語仕様的なお話です。
JavaやPHPに詳しいけど、Pythonはそこまで詳しくはないってエンジニアが、Pythonを書き始めるのって、そこまで苦ではないんですよね。かくゆう私も、最初はJavaやC#を学んで、この業界に入りました。
で、そんなJavaエンジニアだったり、新人の後輩によく聞かれていたのが、
「このソースの一番下にある…
if __name__ == '__main__'
ってなに?」
でした。そして僕はこう答えるのです。
「それはおまじないだよ、Javaでいう
public void static main(String args[])
だよ」
と流していたのでした。 今回はこのPythonのおまじないに真剣に向き合いたいと思います。
おまじないの正体とPythonのimportの仕組み
早速ですがこのおまじないの正体です。
if __name__ == '__main__'
は、Pythonがpython [ファイル名].py
のように、コマンドラインから実行されたときのみ動く処理を書くときに用いる書き方です。
なのでコマンドラインから起動したい処理はこのif内に書いておけばいいわけですね。
でも、プログラムは上から下まで処理されるんだから、わざわざ明示的に書かなくてもよくない?って思いますよね。
実はこれ、Pythonのimportの仕組みと関係しているのです。
Pythonのimportは、importしたファイルを単に実行しているだけなのです。
Pythonは、ファイルをimportする時に実行することで、変数、メソッド、クラスなどをメモリ上に展開して、他のソースでも使えるようにしているんですね。
つまり裏を返せば、Pythonのソースにimport時に実行されるとまずい処理は、このif __name__ == '__main__'
内に書かなくてはならないのです。importはコマンドライン実行ではないので、if文内の処理は実行されません。
試しに以下の2つのソースを用意し、main.py
をコマンドラインで実行してみます。
main.py
import sub if __name__ == '__main__': #sub.py内のメソッドを呼び出す sub.run()
sub.py
def run(): print("sub.pyの処理を実行") # デバッグ用 run()
sub.pyの処理を実行 sub.pyの処理を実行
run()
が2回走りましたね?
そうです、この結果の1行目は、import時にsub.py
の5行目の処理が実行され、2行目の処理は、main.py
の5行目で呼び出しされた結果です。
import=実行であることがわかりましたね。sub.py
を以下のように修正して、再度main.py
をコマンドラインで実行してみます。
def run(): print("sub.pyの処理を実行") if __name__ == '__main__': # デバッグ用 run()
sub.pyの処理を実行
importするファイルにメソッドの実行を書く危険性
実はこれ結構危ないハマりどころかもしれません。
今回のsub.py
のように、メソッドの実行をベタ書きしてしまうと、importされるたびに、メソッドが実行されてしまいます。これによって、意図せぬ処理が走ってしまうんですね。
本当は認証の処理が別のメソッド内で走るのに、実際のロジック実行の処理が先に走ってしまい、エラーを発生させてしまったりと、バグや障害の発生源にもなりうるので、コマンドライン上でのみ実行すると決まりきっている処理は、
if __name__ == '__main__'
内に書くのを推奨します。
nameの正体
ではおまじないの正体がわかったところで、最後に__name__
の正体を見ていきましょう。この変数は、Pythonが予め用意しているグローバル変数になります。__name__
のほかにも、__file__
や、__package__
などがあります。
この__name__
には、コマンドラインから実行されたときは"main"が、importされた場合はモジュール名が設定されます。だから、if __name__ == '__main__'
という構文で、コマンドラインから実行したか否かを判別できるんですね。実験として以下の3つのソースを用意し、main_module.py
をコマンドラインで実行してみます。
main_module.py
import sub_module from src import src_module if __name__ == '__main__': print('main_module') print(__name__) print('sub_module') sub_module.run() print('src.src_module') src_module.run()
sub_module.py
def run(): print(__name__)
src/src_module.py
def run(): print(__name__)
main_module __main__ sub_module sub_module src.src_module src.src_module
ご覧の通り、main_module.py
の__name__
には"main"が、importされた2ファイルは、それぞれモジュール名がセットされているのがわかります。ディレクトリを切っても、その階層まで含んでくれています。
まとめ
今回はif __name__ == '__main__'
の正体と、Pythonのimportについて検証してみました。
importによる実行によって予期せぬ処理を実行してしまわぬよう、if __name__ == '__main__'
を活用して、楽しいPythonライフを送りましょうー!