Python: Was macht if __name__ == "__main__":?

Diese beiden Zeilen findet man am Ende sehr vieler Python-Dateien. Was sie tun, wozu sie gut sind — und eine Folge davon, die man kaum findet, wenn man sie nicht schon einmal erlebt hat.

Was es tut

Jedes Python-Modul hat ein Attribut __name__. Python setzt es auf den Namen des Moduls — außer bei genau der Datei, die du zum Ausführen übergeben hast. Dort steht "__main__".

Der Block läuft also nur, wenn die Datei direkt ausgeführt wird:

def gruesse(name):
    return f"Hallo, {name}!"

if __name__ == "__main__":
    print(gruesse("Welt"))

Die Datei ausführen:

user@pc:~$ python greeting.py
[top level of greeting.py runs; __name__ = '__main__']
Hello, world!
(this block only runs when the file is executed directly)

Dieselbe Datei von woanders importieren:

[top level of greeting.py runs; __name__ = 'greeting']

Die Funktion ist in beiden Fällen definiert. Der Block unter der Abfrage lief nur beim ersten Mal.

Wozu das gut ist

Wichtig ist die Einsicht, dass ein Import jede Zeile des Moduls ausführt. Nicht nur die Definitionen — alles. Hier dasselbe Modul ohne die Abfrage:

def gruesse(name):
    return f"Hallo, {name}!"

print(gruesse("Welt"))

Jetzt importieren:

about to import no_guard ...
[top level of no_guard.py runs; __name__ = 'no_guard']
Hello, world!
(this runs on import too, whether you wanted it or not)

Wer dein Modul importiert hat, um eine einzige Funktion zu benutzen, hat deine Demo-Ausgabe gleich mitbekommen. Ersetz das print durch etwas, das eine Datenbankverbindung öffnet, sys.argv auswertet oder einen Webserver startet, und das Problem ist nicht mehr kosmetisch.

Mit der Abfrage machst du eine Datei gleichzeitig als Bibliothek und als Skript brauchbar.

python -m macht dasselbe

user@pc:~$ python -m greeting
[top level of greeting.py runs; __name__ = '__main__']
Hello, world!

Auch -m setzt __name__ auf "__main__". Der Unterschied liegt nicht im Namen, sondern darin, wie Python das Modul findet — dahinter steckt runpy. -m durchsucht sys.path und stellt das aktuelle Verzeichnis nach vorne, während beim Ausführen einer Datei über den Pfad deren Verzeichnis dort landet. Auf diesen Unterschied geht ein guter Teil der Importfehler zurück, bei denen es “auf die eine Art funktioniert und auf die andere nicht”.

Bei einem Paket sucht -m nach einer Datei namens __main__.py:

user@pc:~$ python -m app
[app/__init__.py runs; __name__ = 'app']
[app/__main__.py runs; __name__ = '__main__']

Beachte die Reihenfolge — zuerst läuft die __init__.py des Pakets, dann die __main__.py. So funktionieren python -m http.server und python -m pip.

Die Falle: eine Datei, zwei Modulobjekte

Und jetzt der Teil, der den Artikel wert ist.

Angenommen eine Datei läuft als __main__, und irgendwo in deinem Programm wird dieselbe Datei zusätzlich unter ihrem echten Namen importiert. Python erkennt nicht, dass es dasselbe ist:

__main__.COUNTER      : ['from __main__']
double_import.COUNTER : ['from the import']
same object?          : False
sys.modules['__main__'] is sys.modules['double_import']? False

Die Datei wurde zweimal ausgeführt, und es gibt jetzt zwei Kopien von allem, was darin steht. Zwei COUNTER-Listen, zwei von jeder Klasse, zwei von jeder Modulkonstanten.

Der Grund ist, dass sys.modules über den Modulnamen adressiert wird. Das laufende Skript ist unter __main__ eingetragen. Importiert später etwas double_import, sucht Python nach diesem Schlüssel, findet ihn nicht und führt die Datei brav ein zweites Mal aus.

Die Symptome vergisst man nicht mehr, wenn man sie einmal hatte:

  • isinstance(obj, MeineKlasse) ist False für ein Objekt, das offensichtlich eine MeineKlasse ist
  • ein Cache oder ein Singleton auf Modulebene existiert doppelt, Schreibzugriffe über den einen sind über den anderen unsichtbar
  • zwei Enum-Mitglieder, die identisch sein müssten, sind es nicht

Die Lösung ist eine Frage der Struktur. Der eigentliche Code gehört in ein Modul, das immer nur importiert wird, und der Einstiegspunkt wird eine dünne Datei, die es importiert:

# app/__main__.py
from app.cli import main

if __name__ == "__main__":
    main()

Damit steht in der Datei, die als __main__ läuft, nichts mehr, worauf es ankommt.

Was in den Block gehört

Wenn möglich eine einzige Zeile:

if __name__ == "__main__":
    main()

Dafür gibt es zwei gute Gründe. Code innerhalb der Abfrage lässt sich von außen weder importieren noch testen — je weniger dort steht, desto besser. Und Namen, die du dort bindest, sind globale Variablen des Moduls, was man leicht vergisst, wenn der Block auf zwanzig Zeilen anwächst.

Noch eine Stelle, an der namemain” ist

Der interaktive Interpreter:

user@pc:~$ echo 'print(f"  __name__ = {__name__!r}")' | python -
  __name__ = '__main__'

Deshalb führt eine eingefügte Datei in der REPL ihren main-Block aus — und eine Zelle in einem Jupyter Notebook genauso.

Hinweis zu Netcup (Werbung)

Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).