Pythonインタープリターがソースファイルを読み取るときは常に、次の2つのことを行います。
これがどのように機能し__name__
、Pythonスクリプトで常に確認されるチェックに関する質問にどのように関連するかを見てみましょう。
コードサンプル
少し異なるコードサンプルを使用して、インポートとスクリプトがどのように機能するかを見てみましょう。次のファイルがと呼ばれているとしますfoo.py
。
# Suppose this is foo.py.
print("before import")
import math
print("before functionA")
def functionA():
print("Function A")
print("before functionB")
def functionB():
print("Function B {}".format(math.sqrt(100)))
print("before __name__ guard")
if __name__ == '__main__':
functionA()
functionB()
print("after __name__ guard")
特殊変数
Python interpeterがソースファイルを読み取るとき、最初にいくつかの特別な変数を定義します。この場合、__name__
変数を考慮します。
モジュールがメインプログラムの場合
モジュール(ソースファイル)をメインプログラムとして実行している場合、たとえば
python foo.py
インタプリタはハードコードされた文字列"__main__"
を__name__
変数に割り当てます、すなわち
# It's as if the interpreter inserts this at the top
# of your module when run as the main program.
__name__ = "__main__"
モジュールが別のモジュールによってインポートされたとき
一方、他のモジュールがメインプログラムであり、モジュールをインポートするとします。これは、メインプログラム、またはメインプログラムがインポートする他のモジュールに次のようなステートメントがあることを意味します。
# Suppose this is in some other main program.
import foo
インタープリターはfoo.py
ファイルを検索し(他のいくつかのバリアントも検索します)、そのモジュールを実行する前に"foo"
、インポートステートメントから__name__
変数に名前を割り当てます。
# It's as if the interpreter inserts this at the top
# of your module when it's imported from another module.
__name__ = "foo"
モジュールのコードの実行
特殊変数が設定された後、インタープリターはモジュール内のすべてのコードを一度に1つのステートメントで実行します。この説明をたどるには、コードサンプルの横にある別のウィンドウを開いてください。
常に
文字列を出力します"before import"
(引用符なし)。
math
モジュールをロードし、という変数に割り当てますmath
。これはimport math
、次のものと置き換えることと同じです(これは__import__
、文字列を取得して実際のインポートをトリガーするPythonの低レベル関数であることに注意してください)。
# Find and load a module given its string name, "math",
# then assign it to a local variable called math.
math = __import__("math")
文字列を出力します"before functionA"
。
def
ブロックを実行して関数オブジェクトを作成し、その関数オブジェクトをと呼ばれる変数に割り当てますfunctionA
。
文字列を出力します"before functionB"
。
2番目のdef
ブロックを実行して、別の関数オブジェクトを作成し、それをと呼ばれる変数に割り当てますfunctionB
。
文字列を出力します"before __name__ guard"
。
モジュールがメインプログラムの場合のみ
- モジュールがメインプログラムである場合は、それ
__name__
が実際にに設定されていることがわかり"__main__"
、文字列"Function A"
とを出力する2つの関数が呼び出されます"Function B 10.0"
。
モジュールが別のモジュールによってインポートされた場合のみ
- (代わりに)モジュールがメインプログラムではなく、別のプログラムによってインポートされた場合
__name__
は"foo"
、ではなく"__main__"
となり、if
ステートメントの本文はスキップされます。
常に
"after __name__ guard"
両方の状況で文字列を出力します。
概要
要約すると、2つのケースで出力される内容は次のとおりです。
# What gets printed if foo is the main program
before import
before functionA
before functionB
before __name__ guard
Function A
Function B 10.0
after __name__ guard
# What gets printed if foo is imported as a regular module
before import
before functionA
before functionB
before __name__ guard
after __name__ guard
なぜこのように機能するのですか?
なぜ誰もがこれを望んでいるのか疑問に思うかもしれません。まあ、.py
他のプログラムやモジュールとしてモジュールとして使用でき、メインプログラム自体としても実行できるファイルを作成したい場合があります。例:
モジュールはライブラリですが、いくつかの単体テストやデモを実行するスクリプトモードが必要です。
モジュールはメインプログラムとしてのみ使用されますが、ユニットテストがいくつかあります。テストフレームワークは.py
、スクリプトなどのファイルをインポートし、特別なテスト関数を実行することで機能します。モジュールをインポートしているからといって、スクリプトの実行を試みたくありません。
モジュールは主にメインプログラムとして使用されますが、上級ユーザー向けのプログラマー向けのAPIも提供します。
これらの例を超えて、Pythonでスクリプトを実行することは、いくつかの魔法の変数を設定してスクリプトをインポートするだけであるのはエレガントです。スクリプトの「実行」は、スクリプトのモジュールをインポートすることの副作用です。
思考の糧
# Suppose this is foo2.py.
def functionA():
print("a1")
from foo2 import functionB
print("a2")
functionB()
print("a3")
def functionB():
print("b")
print("t1")
if __name__ == "__main__":
print("m1")
functionA()
print("m2")
print("t2")
- ここで、
__name__
チェックインを削除するとどうなるかを考えますfoo3.py
。
# Suppose this is foo3.py.
def functionA():
print("a1")
from foo3 import functionB
print("a2")
functionB()
print("a3")
def functionB():
print("b")
print("t1")
print("m1")
functionA()
print("m2")
print("t2")
- スクリプトとして使用するとどうなりますか?モジュールとしてインポートした場合?
# Suppose this is in foo4.py
__name__ = "__main__"
def bar():
print("bar")
print("before __name__ guard")
if __name__ == "__main__":
bar()
print("after __name__ guard")
if __name__ == "__main__":
ブロック条件は、Python 3限り廃止/非推奨とされていますか?私はそれを述べているいくつかの情報を見つけました。