"Zaklęcie" ifname
Zastosowanie if name == "main wg. mnie wymaga dodatkowego wyjaśnienia - zwłaszcza, że pewnie będziecie spotykać się z tą konstrukcją przy pracy w Pythonie.
Zacznijmy od przeanalizowania czemu ta konstrukcja powstała.
Jak działa importowanie
Może Was to nieco zaskoczyć - ale przy użyciu słowa import zawartość importowanego pliku z kodem jest URUCHAMIANA!!!
Załóżmy, że stworzyłem sobie prosty projekt składający się z 2 plików. Pierwszy z nich o nazwie moje_funkcje.py zawiera napisane przeze mnie funkcje, które będę wykorzystywał.
Drugi, o nazwie main.py, to główny plik wykonywalny programu - to właśnie ten plik będę uruchamiał gdy będę chciał skorzystać z tej aplikacji.
Struktura projektu wygląda więc tak:
.
├── main.py
└── moje_funkcje.py
1 directory, 2 filesZawartość main.py wygląda następująco:
from moje_funkcje import *
print("Wpisz liczby do zsumowania:")
a = int(input("1sza liczba: "))
b = int(input("2ga liczba: "))
print(sumuj_liczby(a, b))- czyli nic skomplikowanego. W pierwszej linijce importuję całą zawartość z
moje_funkcje.py.
Zerknijmy więc jak wygląda kod w moje_funkcje.py:
def sumuj_liczby(a, b):
return a + b
print("Poniżej testy funkcji:")
assert sumuj_liczby(2, 3) == 5Zwróćcie uwagę, że poza samą funkcją sumuj_liczby plik ten zawiera też dodatkowe instrukcje - wyświetlające nam informację o przeprowadzeniu testu i sam test z assert.
Zobaczmy co się stanie gdy uruchomię aplikację poprzez python3 main.py:

Zaraz, zaraz - jak to? Przecież w main.py nie robię żadnych testów?! Jedynie importuję funkcję z moje_funkcje i wykorzystuję ją... Skąd ten komunikat?
Na tym właśnie polega problem - importując uruchamiamy wszystko z tego zewnętrznego pliku. Tym samym, mimo tego, że uruchomiłem main.py to wystartowałem wszystko co znajduje się również w moje_funkcje.py - i to właśnie stamtąd pochodzi ten komunikat.
Jak sobie z tym poradzić?
To, że w pliku z którego coś importujemy są też jakieś instrukcje, które są uruchamiane po bezpośrednim jego wywołaniu to nic niezwykłego. Możemy mieć przecież sytuację gdy budujemy sobie kilka skryptów - w zależności od sytuacji raz uruchamiamy jeden plik, innym drugi. Jednocześnie w obu plikach importujemy sobie jakieś funkcje / klasy od sąsiada - tak, żeby nie kopiować tego kodu wiele razy.
Możemy też umieszczać jakieś instrukcje, które ułatwiają nam debugowanie kodu - tak jak w przykładzie powyżej.
Jak w takim razie możemy umieszczać takie fragmenty w sposób, który nie spowoduje ich wywołania przy importowaniu?
Właśnie do tego służy ta dziwna konstrukcja if name == "main".
Zmodyfikuję oba pliki:
- main.py
from moje_funkcje import *
print("\nPlik main.py:")
print(__name__)- moje_funkcje.py
def sumuj_liczby(a, b):
return a + b
print("\nPlik moje_funkcje:")
print(__name__)I uruchomię python3 main.py:
Plik moje_funkcje:
moje_funkcje
Plik main.py:
__main__Zwróćcie uwagę, że zmienna name gdy została wywołana z zaimportowanego pliku (w powyższym przykładzie - moje_funkcje.py) przyjęła jego nazwę (czyli moje_funkcje), podczas gdy wywołana w pliku z którego startujemy aplikację zwróciła nam:
__main__
... i nie, to wynika z tego, że nasz plik nazywa się main.py 😄.
Zawsze jak wywołamy tę zmienną wewnątrz uruchamianego pliku to zwróci nam właśnie:
__main__, a z kolei gdy odwołamy się do niej w jakimś zaimportowanym kodzie to dostaniemy nazwę zaimportowanego pliku.
Właśnie tę właściwość wykorzystujemy, żeby przeciwdziałać uruchamianiu kodu przy imporcie.
Wprowadźmy więc jeszcze raz zmianę w moje_funkcje.py:
def sumuj_liczby(a, b):
return a + b
if __name__ == "__main__":
print("Poniżej testy funkcji:")
assert sumuj_liczby(2, 3) == 5Plik main.py zostawiam bez zmian (chociaż dobrą praktyką byłoby też dodanie do niego tej konstrukcji z `if name` ).
Po uruchomieniu python3 main.py dostaję:

Trochę "magicznych" zabiegów - i problem z importami rozwiązany.