dshimizu/blog

アルファ版

Python の __main__ やトップレベルコードについてのメモ書き

はじめに

Python で出てくる __main__ についてドキュメントを見ていて、「トップレベルコード環境」というものが出てきて、あんまり詳しくわかっていなかったので気になったので調べたことのメモ書きです。

__main__ について

ドキュメントを読むと、__main__ は以下の2つのために使われる、と記載されていました。*1

  1. the name of the top-level environment of the program, which can be checked using the name == 'main' expression; and
  2. the main.py file in Python packages.

__name__ == '__main__' を使用してトップレベルの環境かどうか確認できるようにすることと、Python パッケージの __main__.py で利用するもの、とのことでした。

ちなみに先頭と末尾をアンダースコア __ になっているものは、Python の中で予約されているもののようで、メインプログラムを表すものが __main__ が利用されるようです。

トップレベルと __main__ 変数

例えば以下のようなディレクトリ構成だった場合には、 main.py に相当するものを起点に、同一階層のモジュールや、下位の階層の mypackage のモジュール類を読み込むような形で構成される形が多いと思います。

myproject/
├─ main.py
├─ hogemodule.py
├─ mypackage/
:   ├─ __init__.py
    ├─ __main__.py
    ├─ mymodule.py
    :

Python スクリプトを実行する場合、他の Python スクリプトから import される場合と、何らかで直接 .py のプログラムを直接実行しる場合があるかと思いますが、直接実行する場合に指定されるモジュール(python3 hogemodule.pyの場合は hogemodule)がトップレベルコード*2ということになるようです。

ドキュメントによると、主にインタプリタで実行されるケースはトップレベルコードでの実行ということになるらしいです*3

Python モジュールまたはパッケージがインポートされると、__name__ という変数に Python モジュールの名前に設定されます。 通常、これは .py の拡張子を除いた Python ファイル自体の名前です。例えば hello.py の場合は hello という名前がモジュールとなり、__name__ に設定されます。

ただし、モジュールがトップレベル コード環境で実行される場合、 __name__ 変数には、文字列 __main__ が設定されます。

Python3 のインタプリタを呼び出した時

インタプリタを呼び出して __name__ を出力すると、値は __main__ になっています。

% python3
Python 3.7.9 (v3.7.9:13c94747c7, Aug 15 2020, 01:31:08)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> __name__
'__main__'

直接実行する場合の Python モジュール

適当に以下のようなスクリプトを作成してみます。

#!/usr/local/bin/python3

print(__name__)
print("Hello World!!")

ファイル名.py でモジュールと見なされ、 モジュール名をグローバル変数 __name__ で取得できることが確認できます*4 実行すると __name__ の値は __main__ になっています。

% python3 helloworld.py
__main__
Hello World!!

-m オプションで実行される Python モジュールまたはパッケージ

実行してみると、こちらも __name__ の値は __main__ になっています。

% python3 -m helloworld
__main__
Hello World!!

標準入力から Python インタプリタへ渡されたコード

読み込んだモジュールはそのままモジュールとして扱われており、標準入力で渡したコード自体は __main__ と判定されているようです。

% echo "import helloworld \nprint(__name__)" | python3
helloworld
Hello World!!
__main__

-c オプションでのPythonインタプリタに渡されたコード

-c オプションの場合も同様に、読み込んだモジュールはそのままモジュールとして扱われており、標準入力で渡したコード自体は __main__ と判定されているようです。

% python3 -c "import helloworld
print(__name__)"
helloworld
Hello World!!
__main__

まとめ

ドキュメントに書いてあることですが、Python__main__ に関して、以下のことを確認してみました。

  • 主に Python インタプリタを利用する場合に指定されるモジュール( ex) hogehoge.py)がトップレベルコードとなる
  • Python モジュールは、ファイルの引数として Python インタプリタに渡される場合や -m オプションの引数として指定される場合には __main__ となる
  • Python パッケージは -m オプションの引数として指定される場合には __main__ となる
  • Python コードは、-c オプションや標準入力から Python インタプリタに渡される場合には __main__ となる

モジュールは、自身の __name__ を以下のようなコードでチェックすることで、何らかの形で直接実行されているのか他のモジュール等から呼び出されて実行されているかを判定できるため、テスト時等他のモジュールから Import されずに実行される場合には所定のメソッドを呼び出す、といった場合などに実行させたい処理を記述できます。

if __name__ == '__main__':
    # exec 

参考