「if __name__ == ‘__main__’をかっこよく書けるautomain | TRIVIAL TECHNOLOGIES on CLOUD」で紹介されていたautomainモジュールについて調べてみた。
Pythonでモジュールが直接実行されたかどうかを判断するとき、一般的には次のように記述する。
def main():
# do something
if __name__ == '__main__':
main()
automainモジュールを使うと、これを次のようにかっこよく書くことができる。
from automain import *
@automain
def main():
# do something
automainモジュールのコードを読む前に、ちょっとおさらい。
__name__には、直接実行されたときには’__main__’、importされたときはモジュール名が入る。
test.py
def main():
print __name__
if __name__ == '__main__':
main()
sample.py
import test
test.main()
直接実行したときには、__main__が出力される。
$ python test.py
__main__
importしたときは、モジュール名’test’が出力される。
$ python sample.py
test
だから「if __name__ == ‘__main__’:」でモジュールが直接実行されたかどうかがわかる。
さて、automainモジュールを見てみよう。
def automain(func):
import inspect
parent = inspect.stack()[1][0]
name = parent.f_locals.get('__name__', None)
if name == '__main__':
func()
まず最初の行から。
import inspect
inspectモジュールは、型チェック・ソースコードの取得・クラス/関数から情報を取得・インタープリタのスタック情報の調査、の機能を持つ。
次の行。
parent = inspect.stack()[1][0]
inspect.stack()は呼び出し元スタックのフレームレコードのリストを返す。
フレームレコードは長さ6のタプルで、フレームオブジェクト・ファイル名・実行中の行番号・関数名・コンテキストのソース行のリスト・ソース行リストの実行中行のインデックスを格納している。
inspect.stack()の返値のリストの値は、[automainモジュールのフレームレコード,automain関数呼び出し元のフレームレコード]となる。
フレームレコードの最初の要素はフレームオブジェクトだから、parentはautomain関数の呼び出し元のフレームオブジェクトになる。
次の行を見てみよう。
name = parent.f_locals.get('__name__', None)
f_localsはフレームで参照しているローカル名前空間。
そこから’__name__’の値を取得している。
if name == '__main__':
で、’__main__’と比較して、直接実行されたかどうかを判断するという寸法だ。