__main__ --- トップレベルのコード環境


Python では、 __main__ という特別な名前が次の二つの重要な用途で使われます:

  1. プログラムのトップレベル環境の名前。 __name__ == '__main__' という式でチェックすることができる。

  2. Python パッケージにおける __main__.py ファイル。

どちらも Python のモジュールに関わる機能です。 1つ目はユーザーがどうモジュールを使うか、2つ目はモジュールとモジュールがどうやりとりするかに関係します。 詳細は以下で説明します。 Python モジュールがどういうものかについては、 モジュール を参照してください。

__name__ == '__main__'

Python モジュールやパッケージがインポートされるとき、 __name__ の値はそのモジュールの名前となります。 通常、インポートされる Python ファイル自体のファイル名から拡張子 .py を除いたものとなります:

>>> import configparser
>>> configparser.__name__
'configparser'

インポートされるファイルがパッケージの一部である場合は、 __name__ にはそのパッケージのパスも含まれます:

>>> from concurrent.futures import process
>>> process.__name__
'concurrent.futures.process'

しかし、モジュールがトップレベルのスクリプト環境で実行される場合は、 __name__ が '__main__' という文字列になります。

「トップレベルのスクリプト環境」とは

__main__ は、トップレベルのコードが実行される環境の名前です。 "トップレベルのコード" は、実行を開始する最初のユーザー指定の Python モジュールです。これは、このモジュールがプログラムに必要なすべての他のモジュールをインポートするために、 "トップレベル" なのです。時折、 "トップレベルのコード" は、アプリケーションには エントリーポイント と呼ばれます。

以下のものがトップレベルのスクリプト環境となります:

  • インタラクティブプロンプトのスコープ:

    >>> __name__
    '__main__'
    
  • Python インタープリタにファイル引数として渡される Python モジュール:

    $ python helloworld.py
    Hello, world!
    
  • Python インタープリタにPython -m オプションとして渡される Python モジュールまたはパッケージ:

    $ python -m tarfile
    usage: tarfile.py [-h] [-v] (...)
    
  • 標準入力から Python インタープリタが読み込む Python コード:

    $ echo "import this" | python
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    ...
    
  • Python インタープリタに -c オプションで渡される Python コード:

    $ python -c "import this"
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    ...
    

上記それぞれの場合で、トップレベルのモジュールの __name__ の値が '__main__' となります。

これにより、 __name__ をチェックすれば各モジュールは自分がトップレベル環境で実行されているかどうかを知ることができます。 このことから、モジュールが import 文で初期化された場合以外の場合でのみコードを実行するため、次のコードがしばしば用いられます:

if __name__ == '__main__':
    # モジュールが import 文で初期化された場合以外に実行する。
    ...

参考

あらゆる場合に __name__ の値がどうセットされるのかについて、詳しくはチュートリアルの モジュール セクションを参照してください。

通常の使われ方

一部のモジュールでは、コマンドライン引数をパースしたり標準入力からデータを取得したなど、スクリプト用途のみのコードが含まれています。 このようなモジュールが、例えばユニットテストのため、別のモジュールからインポートされると、そのスクリプト用コードが意図に反して実行されてしまいます。

if __name__ == '__main__' というコードは、このようなときに役立ちます。 このブロックの中にあるコードは、当該のモジュールがトップレベル環境で実行されていない限り、実行されません。

if __name__ == '__main__' の下のブロックにあるコードはできるだけ少なくした方が、コードの分かりやすさや正確さにつながります。 最もよくあるのが、プログラムの主要な処理を main 関数の中にカプセル化することです:

# echo.py

import shlex
import sys

def echo(phrase: str) -> None:
   """print を呼び出すだけのダミーのラッパー。"""
   # 説明のため、この関数に有用で再利用可能な処理が
   # 含まれていると考える
   print(phrase)

def main() -> int:
    """入力された引数を標準出力に出力する"""
    phrase = shlex.join(sys.argv)
    echo(phrase)
    return 0

if __name__ == '__main__':
    sys.exit(main())  # sys.exit の使い方は次の節で説明

注意すべき点として、もし main 関数内のコードをカプセル化せず if __name__ == '__main__' の下に直接書いた場合、 phrase 変数はモジュール全体からグローバルにアクセスできてしまいます。 モジュール内の他の関数が意図せずローカル変数ではなくそのグローバル変数を使用してしまう可能性があるため、ミスにつながります。 main 関数を用意することでこの問題は解決できます。

main 関数を使うことのもう一つのメリットとして、 echo 関数が分離し、別の場所からインポートできるようになることです。 echo.py がインポートされるとき、 echo 関数と main 関数が定義されますが、 __name__ != '__main__' であるため、どちらの関数も呼び出されません。

パッケージングで考慮すべき点

main 関数は、コンソールスクリプトのエントリーポイントに指定して、コマンドラインツールを作るためによく使われます。 この場合、pip は関数呼び出しをテンプレートのスクリプトに挿入し、main の戻り値を sys.exit() に渡します。 例えば、次のようになります。

sys.exit(main())

main の呼び出しは sys.exit() で囲まれるため、関数は sys.exit() の入力として受け付けられる値を返すことが想定されています。 通常は整数か None です (関数に return 文がない場合は暗黙に None を返します)。

最初からこの慣習に従っておくと、モジュールを直接実行した場合 (python echo.py など) と、後で pip でインストール可能なパッケージのコンソールスクリプトのエントリーポイントとして配布した場合とで、同じ動作になります。

特に、main 関数から文字列を返す場合は注意してください。 sys.exit() は文字列の引数を失敗のメッセージとして扱うため、プログラムは失敗を示す終了コード 1 で終了し、その文字列は sys.stderr に出力されます。 先ほどの echo.py は、sys.exit(main()) の慣習に従った例です。

参考

Python Packaging User Guide には、現在使われているツールで Python パッケージを配布・インストールする方法について、チュートリアルとリファレンスがまとめられています。

Python パッケージの __main__.py

Python パッケージについてよく知らない場合は、チュートリアルの パッケージ を参照してください。 __main__.py ファイルは、主にパッケージのコマンドラインインターフェースを提供するために使われます。 次のような「bandclass」という架空のパッケージを考えてみます。

bandclass
  ├── __init__.py
  ├── __main__.py
  └── student.py

コマンドラインから -m フラグでパッケージ自体を直接実行すると、__main__.py が実行されます。 例を示します。

$ python -m bandclass

このコマンドで __main__.py が実行されます。 この仕組みの使い方は、作成するパッケージの性質によります。 この架空の例では、教師が生徒を検索できるようにするとよいでしょう。

# bandclass/__main__.py

import sys
from .student import search_students

student_name = sys.argv[1] if len(sys.argv) >= 2 else ''
print(f'Found student: {search_students(student_name)}')

from .student import search_students は相対インポートの例です。 この書き方は、パッケージ内のモジュールを参照するときに使えます。 詳しくは、チュートリアルの モジュール にある パッケージ内参照 を参照してください。

通常の使われ方

通常、__main__.py の内容は if __name__ == '__main__' ブロックでは囲みません。 代わりに、ファイルを短く保ち、実行する関数を他のモジュールからインポートします。 そうすれば、それらのモジュールを簡単にユニットテストでき、再利用もしやすくなります。

パッケージ内の __main__.py で if __name__ == '__main__' ブロックを使った場合も、期待どおりに動作します。 インポートされたときには、__name__ 属性にパッケージのパスが含まれるためです。

>>> import asyncio.__main__
>>> asyncio.__main__.__name__
'asyncio.__main__'

ただし、.zip ファイルのルートディレクトリにある __main__.py では、この方法は使えません。 そのため、一貫性を保つには、__name__ のチェックを含まない最小限の __main__.py にする方がよいでしょう。

参考

標準ライブラリ内で最小限の __main__.py を持つパッケージの例は、venv を参照してください。 if __name__ == '__main__' ブロックは含まれていません。 python -m venv [directory] で実行できます。

インタープリタ実行ファイルの -m フラグの詳細は、runpy を参照してください。

.zip ファイルにまとめたアプリケーションの実行方法は、zipapp を参照してください。 この場合、Python はアーカイブのルートディレクトリにある __main__.py を探します。

import __main__

Python プログラムをどのモジュールから起動したかにかかわらず、同じプログラム内の他のモジュールは、__main__ モジュールをインポートすることでトップレベル環境のスコープ (名前空間) をインポートできます。 これは __main__.py というファイルのインポートではなく、特別な名前 '__main__' が割り当てられたモジュールのインポートです。

__main__ の名前空間を利用するモジュールの例を示します。

# namely.py

import __main__

def did_user_define_their_name():
    return 'my_name' in dir(__main__)

def print_user_name():
    if not did_user_define_their_name():
        raise ValueError('Define the variable `my_name`!')

    print(__main__.my_name)

このモジュールは、例えば次のように使えます。

# start.py

import sys

from namely import print_user_name

# my_name = "Dinsdale"

def main():
    try:
        print_user_name()
    except ValueError as ve:
        return str(ve)

if __name__ == "__main__":
    sys.exit(main())

このプログラムを起動すると、次のような結果になります。

$ python start.py
Define the variable `my_name`!

プログラムの終了コードは、エラーを示す 1 になります。 my_name = "Dinsdale" の行のコメントを外すとプログラムは正しく動作し、成功を示す終了コード 0 で終了します。

$ python start.py
Dinsdale

__main__ をインポートしても、start モジュールの if __name__ == "__main__" ブロックにある、スクリプト用のトップレベルコードが意図せず実行されることはありません。 なぜこのように動作するのでしょうか。

Python はインタープリタの起動時に空の __main__ モジュールを sys.modules に追加し、トップレベルコードを実行してその内容を埋めていきます。 この例では、start モジュールが一行ずつ実行され、namely をインポートします。 今度は namely が __main__ (実体は start) をインポートするので、循環インポートになります。 しかし、内容が部分的に埋まった __main__ モジュールが sys.modules にあるため、Python はそれを namely に渡せます。 詳しい仕組みは、インポートシステムのリファレンスの __main__ に関する特別な考慮事項 を参照してください。

Python の REPL も「トップレベル環境」の一例なので、REPL で定義したものはすべて __main__ のスコープの一部になります。

>>> import namely
>>> namely.did_user_define_their_name()
False
>>> namely.print_user_name()
Traceback (most recent call last):
...
ValueError: Define the variable `my_name`!
>>> my_name = 'Jabberwocky'
>>> namely.did_user_define_their_name()
True
>>> namely.print_user_name()
Jabberwocky

__main__ のスコープは、pdb と rlcompleter の実装で使われています。