Co robi if __name__ == "__main__":?

Co robi polecenie 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))
Rozwiązanie

Kiedy interpreter Pythona wczytuje plik źródłowy, robi dwie rzeczy:

  • ustawia kilka specjalnych zmiennych, takich jak __name__, a następnie
  • wykonuje cały kod znajdujący się w pliku.

Zobaczmy, jak to działa i jak to się ma do Twojego pytania o sprawdzanie __name__, które zawsze widzimy w skryptach Pythona. Przykładowy kod

Użyjmy nieco innej próbki kodu, aby zbadać, jak działa import i skrypty. Załóżmy, że w pliku o nazwie foo.py znajduje się następująca treść.

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

Zmienne specjalne

Kiedy interpreter Pythona wczytuje plik źródłowy, najpierw definiuje kilka zmiennych specjalnych. W tym przypadku zależy nam na zmiennej __name__. Gdy Twój moduł jest programem głównym Jeśli uruchamiasz swój moduł (plik źródłowy) jako program główny, np.

python foo.py

to interpreter przypisze ciąg "__main__" do zmiennej __name__, tzn.

# It's as if the interpreter inserts this at the top
# of your module when run as the main program.
__name__ = "__main__" 

Kiedy twój moduł jest importowany przez inny Z drugiej strony, załóżmy, że jakiś inny moduł jest programem głównym i importuje twój moduł. Oznacza to, że w głównym programie lub w innym module, który importuje główny program, znajduje się taka instrukcja:

# Suppose this is in some other main program.
import foo

W tym przypadku, interpreter spojrzy na nazwę pliku twojego modułu, foo.py, usunie .py i przypisze ten łańcuch do zmiennej __name__ twojego modułu' tzn.

# It's as if the interpreter inserts this at the top
# of your module when it's imported from another module.
__name__ = "foo"

Wykonywanie kodu modułu

Po skonfigurowaniu zmiennych specjalnych, interpreter wykonuje cały kod modułu, po jednej instrukcji na raz. Być może zechcesz otworzyć inne okno na stronie z przykładem kodu, aby móc śledzić to wyjaśnienie. Zawsze

  1. Wypisuje łańcuch "przed importem" (bez cudzysłowów).
  2. Ładuje moduł math i przypisuje go do zmiennej o nazwie math. Jest to równoważne zastąpieniu import math następującym (zauważ, że __import__ jest niskopoziomową funkcją w Pythonie, która pobiera łańcuch i wywołuje rzeczywisty import):
    # Znajdź i załaduj moduł podając jego nazwę łańcuchową, "math",
    # a następnie przypisz go do zmiennej lokalnej o nazwie math.
    math = __import__("math")
  3. Wypisuje ciąg "przed funkcjąA".
  4. Wykonuje blok def, tworząc obiekt funkcji, a następnie przypisując ten obiekt funkcji do zmiennej o nazwie functionA.
  5. Wypisuje łańcuch "before functionB".
  6. Wykonuje drugi blok def, tworząc kolejny obiekt funkcji, a następnie przypisując go do zmiennej o nazwie functionB.
  7. Wypisuje ciąg "before __name__ guard". Tylko wtedy, gdy twój moduł jest programem głównym.
  8. Jeśli twój moduł jest programem głównym, to zobaczy, że __name__ rzeczywiście został ustawiony na "__main__" i wywoła dwie funkcje, wypisując łańcuchy "Funkcja A" i "Funkcja B 10.0". Tylko wtedy, gdy twój moduł jest importowany przez inny.
  9. (instead) Jeśli twój moduł nie jest głównym programem, ale został zaimportowany przez inny, wtedy __name__ będzie "foo", a nie "__main__", i'pominie ciało instrukcji if. Zawsze
  10. Wypisuje łańcuch "po __name__ guard" w obu sytuacjach. Podsumowanie Podsumowując, oto'co'zostałoby wydrukowane w tych dwóch przypadkach:
# 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

Dlaczego to działa w ten sposób?

Możesz naturalnie zastanawiać się, dlaczego ktokolwiek chciałby tego. Otóż, czasami chcesz napisać plik .py, który może być zarówno używany przez inne programy i/lub moduły jako moduł, jak i może być uruchamiany jako program główny. Przykłady:

  • Twój moduł jest biblioteką, ale chcesz mieć tryb skryptowy, w którym uruchamia on jakieś testy jednostkowe lub demo.

  • Twój moduł jest używany tylko jako program główny, ale ma kilka testów jednostkowych, a framework testowy działa poprzez importowanie plików .py jak twój skrypt i uruchamianie specjalnych funkcji testowych. Nie chcesz, aby próbował on uruchomić skrypt tylko dlatego, że importuje moduł.

  • Twój moduł jest głównie używany jako program główny, ale zapewnia również przyjazne programistom API dla zaawansowanych użytkowników. Poza tymi przykładami, eleganckie jest to, że uruchomienie skryptu w Pythonie polega na ustawieniu kilku magicznych zmiennych i zaimportowaniu skryptu. "Uruchomienie" skryptu jest efektem ubocznym zaimportowania modułu skryptu'. Pokarm do przemyśleń

  • Pytanie: Czy mogę mieć wiele bloków sprawdzających __name__? Odpowiedź: Jest to dziwne, ale język nie będzie cię powstrzymywał.

  • Przypuśćmy, że poniższy tekst znajduje się w pliku foo2.py. Co się stanie, jeśli powiesz python foo2.py w wierszu poleceń? Dlaczego?

# 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")
  • Teraz dowiedz się, co się stanie, jeśli usuniesz sprawdzanie __name__ w 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")
  • Co to zrobi, gdy będzie używane jako skrypt? Gdy zostanie zaimportowane jako moduł?
# Suppose this is in foo4.py
__name__ = "__main__"

def bar():
    print("bar")

print("before __name__ guard")
if __name__ == "__main__":
    bar()
print("after __name__ guard")
Komentarze (6)

Kiedy twój skrypt jest uruchamiany przez przekazanie go jako polecenia do interpretera Pythona,

python myscript.py

wykonywany jest cały kod, który znajduje się na poziomie wcięcia 0. Funkcje i klasy, które są zdefiniowane, są, no cóż, zdefiniowane, ale żaden z ich kodu nie zostanie wykonany. W przeciwieństwie do innych języków, nie ma funkcji main(), która jest uruchamiana automatycznie - funkcja main() jest domyślnie całym kodem na najwyższym poziomie.

W tym przypadku, kodem najwyższego poziomu jest blok if. name__ jest wbudowaną zmienną, która odpowiada nazwie bieżącego modułu. Jednakże, jeśli moduł jest uruchamiany bezpośrednio (jak w myscript.py powyżej), wtedy __name__ jest ustawiane na łańcuch "__main__". Tak więc, możesz sprawdzić czy twój skrypt jest uruchamiany bezpośrednio, czy też jest importowany przez coś innego, testując

if __name__ == "__main__":
    ...

Jeśli twój skrypt jest importowany do innego modułu, jego różne definicje funkcji i klas zostaną zaimportowane i jego kod najwyższego poziomu zostanie wykonany, ale kod w then-body powyższej klauzuli if nie zostanie uruchomiony, ponieważ warunek nie jest spełniony. Jako podstawowy przykład, rozważmy następujące dwa skrypty:

# file one.py
def func():
    print("func() in one.py")

print("top-level in one.py")

if __name__ == "__main__":
    print("one.py is being run directly")
else:
    print("one.py is being imported into another module")
# file two.py
import one

print("top-level in two.py")
one.func()

if __name__ == "__main__":
    print("two.py is being run directly")
else:
    print("two.py is being imported into another module")

Teraz, jeśli wywołasz interpreter jako

python one.py

Wyjściem będzie

top-level in one.py
one.py is being run directly

Jeśli zamiast tego uruchomisz two.py:

python two.py

Otrzymasz

top-level in one.py
one.py is being imported into another module
top-level in two.py
func() in one.py
two.py is being run directly

Tak więc, kiedy moduł one zostaje załadowany, jego __name__ równa się "one" zamiast "__main__".

Komentarze (0)

if __name__ == "__main__" jest częścią, która jest uruchamiana, gdy skrypt jest uruchamiany z (powiedzmy) linii poleceń za pomocą komendy takiej jak python myscript.py.

Komentarze (1)