1. ホーム
  2. パイソン

[解決済み】if __name__ == "__main__": は何をするのでしょうか?

2022-03-17 04:55:33

質問

次のコードでは if __name__ == "__main__": を行うか?

# Threading example
import time, thread

def myfunction(string, sleeptime, lock, *args):
    while True:
        lock.acquire()
        time.sleep(sleeptime)
        lock.release()
        time.sleep(sleeptime)

if __name__ == "__main__":
    lock = thread.allocate_lock()
    thread.start_new_thread(myfunction, ("Thread #: 1", 2, lock))
    thread.start_new_thread(myfunction, ("Thread #: 2", 2, lock))

解決方法は?

簡単な答え

これは、ユーザーが意図しない時に誤ってスクリプトを起動してしまわないようにするための定型的なコードです。ここでは、スクリプトからガードが省略された場合によくある問題を紹介します。

  • 他のスクリプトでガードレススクリプトをインポートした場合(例. import my_script_without_a_name_eq_main_guard を実行するようトリガーします。 インポート時に 2番目のスクリプトのコマンドライン引数を使用する . これはほとんどの場合、間違いです。

  • ガードレススクリプトにカスタムクラスがあり、それをpickleファイルに保存した場合、他のスクリプトでそれをアンピックルすると、ガードレススクリプトのインポートがトリガーされ、前の弾丸で説明したのと同じ問題が発生します。

長い回答

なぜ、そしてどのようにこれが重要なのかをよりよく理解するためには、一歩下がって、Pythonがどのようにスクリプトを初期化し、これがどのようにモジュールのインポート機構と相互作用するのかを理解する必要があります。

Pythonインタプリタがソースファイルを読むときはいつも、次の2つのことを行います。

  • のようないくつかの特殊な変数を設定します。 __name__ を実行し、その後

  • は、そのファイルにあるすべてのコードを実行します。

これがどのように機能するのか、また、あなたが質問した __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インタプリタがソースファイルを読み込むとき、最初にいくつかの特殊変数を定義します。この場合、私たちが気にするのは __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" をimport文から __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文ずつ実行します。この説明を追えるように、コードサンプルのある側で別のウィンドウを開いておくとよいでしょう。

常に

  1. 文字列を表示します。 "before import" (引用符なし)。

  2. を読み込むのです。 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")

  1. 文字列を表示します。 "before functionA" .

  2. を実行するのです。 def ブロックを作成し、関数オブジェクトを作成し、その関数オブジェクトを変数 functionA .

  3. 文字列を表示します。 "before functionB" .

  4. を実行し、2番目の def ブロックの中で、別の関数オブジェクトを作成し、それを変数 functionB .

  5. 文字列を表示します。 "before __name__ guard" .

モジュールがメインプログラムである場合のみ

  1. あなたのモジュールがメインプログラムである場合、次のように表示されます。 __name__ は確かに "__main__" という文字列を表示し、2つの関数を呼び出しています。 "Function A""Function B 10.0" .

自分のモジュールが他のモジュールからインポートされた場合のみ

  1. ( その代わり ) あなたのモジュールがメインプログラムではなく、他のプログラムによってインポートされたものである場合 __name__"foo" でなく "__main__" の本文はスキップされます。 if ステートメントを使用します。

常に

  1. 文字列が表示されます "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でスクリプトを実行することは、いくつかのマジック変数を設定し、スクリプトをインポートするだけでエレガントです。 "実行"スクリプトは、スクリプトのモジュールをインポートすることの副次的効果です。

思考の糧

  • 質問です。複数の __name__ をチェックするブロックがありますか? 回答:そうするのはおかしいですが、言語が止めることはありません。

  • に以下のようなものがあるとします。 foo2.py . という場合はどうなるのでしょうか? python foo2.py をコマンドラインで実行できますか?なぜですか?

# Suppose this is foo2.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters

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.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters

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")