"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 files
Zawartość 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) == 5
Zwróć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) == 5
Plik 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.