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
Maciej – redaktor, pasjonat technologii i samozwańczy pogromca błędów w systemie Windows. Zna Linuxa lepiej niż własną lodówkę, a kawa to jego główne źródło zasilania. Pisze, testuje, naprawia – i czasem nawet wyłącza i włącza ponownie. W wolnych chwilach udaje, że odpoczywa, ale i tak kończy z laptopem na kolanach.