Zrozumienie, czy __name__ == '__main__’ w Pythonie

Photo of author

By maciekx

Zrozumienie konstrukcji if __name__ == '__main__’ w Pythonie

Ten artykuł ma za zadanie wyjaśnić działanie i znaczenie konstrukcji `if __name__ == '__main__’` w języku Python. Jeśli kiedykolwiek przeglądałeś kod w Pythonie, natknąłeś się zapewne na ten warunek w różnych modułach. W kolejnych akapitach przyjrzymy się, co dokładnie on oznacza i kiedy może okazać się przydatny.

Zaczynajmy!

Jakie jest znaczenie zmiennej __name__ w Pythonie?

W Pythonie moduł to po prostu plik z rozszerzeniem `.py`, który zawiera definicje funkcji, instrukcje do wykonania oraz inne elementy kodu. Na przykład, plik o nazwie `hello_world.py` jest modułem o nazwie `hello_world`.

Kiedy uruchamiasz moduł, interpreter Pythona ustawia specjalne zmienne, w tym `__name__`. Aby zrozumieć, jak działa `__name__`, należy poznać mechanizm importowania modułów w Pythonie.

Przykładowe pliki możesz pobrać tutaj. Zajrzyj do folderu `przykład-1`. Znajduje się tam plik `module1.py`. Zmienna `__name__` jest zdefiniowana w przestrzeni nazw danego modułu.

Ten moduł wyświetla na ekranie tekst oraz wartość zmiennej `__name__`.

# example-1/module1.py
print("To jest module1.")
print(f"Zmienna __name__ w module1 ma wartość: {__name__}.")

Uruchommy teraz `module1` z poziomu wiersza poleceń.

$ python module1.py

W wynikach widać, że zmienna `__name__` ma wartość `__main__`.

To jest module1.
Zmienna __name__ w module1 ma wartość: __main__.

Importowanie modułów w Pythonie

Poza bezpośrednim uruchamianiem modułów, często chcemy korzystać z funkcjonalności zdefiniowanych w innych modułach. Python umożliwia to poprzez mechanizm importowania.

Importowanie pozwala używać funkcji, klas i innych elementów z innego modułu bez konieczności kopiowania kodu.

Spójrzmy na plik `module2.py`. Wewnątrz tego pliku zaimportowany został moduł `module1`.

# example-1/module2.py
import module1 # Import modułu module1
print(f"To jest module2")
print(f"Zmienna __name__ w module2 ma wartość: {__name__}.")

Uruchamiamy `module2.py` i obserwujemy wyniki.

$ python module2.py

Wynik pokazuje, że:

  • `module1` jest uruchamiany przy okazji importowania do `module2`, a jego dane wyjściowe są drukowane.
  • Tym razem zmienna `__name__` w `module1` nie ma wartości `__main__`, ale `module1`.
  • Ponieważ `module2` został uruchomiony bezpośrednio, zmienna `__name__` w `module2` ma wartość `__main__`.
Wynik
To jest module1.
Zmienna __name__ w module1 ma wartość: module1.
To jest module2
Zmienna __name__ w module2 ma wartość: __main__.

Kluczowe wnioski:

– Jeśli moduł jest uruchamiany bezpośrednio, jego zmienna `__name__` ma wartość `__main__`.

– Jeśli moduł jest importowany w innym module, jego zmienna `__name__` ma wartość będącą nazwą modułu.

Praktyczne zastosowanie if __name__ == '__main__’ w Pythonie

W tej sekcji zobaczymy, jak można wykorzystać warunek `if __name__ == '__main__’` w praktyce. Zdefiniujemy prostą funkcję, a następnie napiszemy dla niej testy jednostkowe.

Kod można pobrać tutaj, znajduje się on w folderze `example-2`.

Plik `add.py` zawiera definicję funkcji `add_ab()`. Funkcja `add_ab()` przyjmuje dwie liczby i zwraca ich sumę.

# example-2/add.py
def add_ab(a, b):
    return a + b

Użyjemy modułu `unittest` do przetestowania funkcji `add_ab()`.

Pisanie testów jednostkowych dla funkcji Pythona

Spójrzmy na zawartość modułu `test_add.py`.

# example-2/test_add.py
import unittest
from add import add_ab

class TestAdd(unittest.TestCase):
    def test_add_23(self):
        self.assertEqual(add_ab(2, 3), 5)
    
    def test_add_19(self):
        self.assertEqual(add_ab(1, 9), 10)
    
    def test_add_1_minus7(self):
        self.assertEqual(add_ab(1, -7), -6)

Powyższy kod robi następujące rzeczy:

  • Importuje wbudowany moduł do testowania jednostkowego `unittest`
  • Importuje funkcję `add_ab()` z modułu `add`
  • Definiuje klasę testową `TestAdd`, gdzie przypadki testowe są metodami

Aby utworzyć testy jednostkowe dla kodu, należy najpierw zdefiniować klasę testową, która dziedziczy po `unittest.TestCase`. Wszystkie przypadki testowe powinny być zdefiniowane jako metody wewnątrz tej klasy i powinny zaczynać się od `test_`. Jeśli nazwa metody nie zacznie się od `test_`, testy nie zostaną wykryte i uruchomione.

Spróbujmy uruchomić moduł `test_add` z terminala.

$ python test_add.py

Zauważysz, że nie ma żadnego wyjścia, żaden z testów nie został uruchomiony.

Dlaczego tak się dzieje? 🤔

Dzieje się tak dlatego, że aby uruchomić testy, musimy uruchomić `unittest` jako główny moduł, tak jak poniżej:

$ python -m unittest test_add.py

Po uruchomieniu tego polecenia, widzimy, że wszystkie trzy testy zakończyły się pomyślnie.

Wynik
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

Byłoby jednak wygodniej, gdyby testy uruchamiały się po prostu po uruchomieniu modułu `test_add`. Zobaczmy, jak to zrobić.

Użycie if __name__ == '__main__’ do uruchomienia testów jako modułu głównego

Jeśli chcemy, aby testy jednostkowe uruchamiały się bezpośrednio po uruchomieniu modułu, możemy dodać warunek:

# example-2/test_add.py
import unittest
from add import add_ab

class TestAdd(unittest.TestCase):
    def test_add_23(self):
        self.assertEqual(add_ab(2, 3), 5)
    
    def test_add_19(self):
        self.assertEqual(add_ab(1, 9), 10)
    
    def test_add_1_minus7(self):
        self.assertEqual(add_ab(1, -7), -6)

if __name__ == '__main__':
    unittest.main()

Ten warunek informuje interpreter Pythona: „Jeśli ten moduł jest uruchamiany bezpośrednio, uruchom kod `unittest.main()`.”

Po dodaniu tych linijek, możesz uruchomić moduł `test_add` w zwykły sposób.

$ python test_add.py

▶️ Bezpośrednie uruchomienie `test_add` spowoduje uruchomienie wszystkich trzech zdefiniowanych testów.

Wynik
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

Wynik `OK` oznacza, że wszystkie testy zostały wykonane pomyślnie. Trzy kropki wskazują, że przeprowadzono trzy testy i wszystkie zakończyły się sukcesem.

Teraz, zmieńmy oczekiwaną wartość testu `test_add_1_minus7` na 8. Ponieważ funkcja w tym przypadku zwraca -6, jeden z testów powinien zakończyć się niepowodzeniem.

def test_add_1_minus7(self):
        self.assertEqual(add_ab(1, -7), 8)

Jak widać na poniższym wyniku, otrzymujemy `.F.`, jeden z trzech testów zakończył się niepowodzeniem (drugi test) i w śledzeniu otrzymujemy `AssertionError` – ` -6 != 8`.

Wynik
.F.
======================================================================
FAIL: test_add_1_minus7 (__main__.TestAdd)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_add.py", line 12, in test_add_1_minus7
    self.assertEqual(add_ab(1,-7), 8)
AssertionError: -6 != 8

----------------------------------------------------------------------
Ran 3 tests in 0.021s

FAILED (failures=1)

Warto zauważyć, że testy niekoniecznie są uruchamiane w tej samej kolejności, w jakiej zostały zdefiniowane w klasie testów. W powyższym przykładzie `test_add_1_minus7` jest zdefiniowany jako trzecia metoda w klasie, ale został uruchomiony jako druga.

Podsumowanie

Mam nadzieję, że ten artykuł pomógł Ci zrozumieć, jak działa warunek `if __name__ == '__main__’` w Pythonie.

Oto krótkie podsumowanie:

  • Interpreter Pythona ustawia zmienną `__name__` przed wykonaniem skryptu.
  • Gdy uruchamiasz moduł bezpośrednio, wartość `__name__` to `__main__`.
  • Kiedy importujesz moduł do innego skryptu, wartością `__name__` jest nazwa modułu.
  • Możesz użyć `if __name__ == '__main__’` do kontrolowania, które części modułu są uruchamiane podczas bezpośredniego uruchomienia i importu.

Zachęcam do dalszego zgłębiania tajników Pythona i życzę owocnej nauki! 🎉


newsblog.pl