Python-Kurs / Pakete strukturieren und Programme mit __main__ starten
Ein Paket fasst zusammengehörige Module unter einem gemeinsamen Namen zusammen. Eine Funktion main() und ein ausdrücklich kontrollierter Einstieg erlauben es, die Anwendung auszuführen und ihre Module zugleich ohne automatische Programmausgabe wiederzuverwenden.
Ein reguläres Paket im Projektordner anlegen
Erstellen Sie einen neuen Ordner project und darin den Unterordner expense_app. Diese Übung verwendet ein reguläres Paket mit __init__.py. Es gibt auch Namespace-Pakete ohne diese Datei; hier ist die ausdrücklich sichtbare einfache Struktur leichter nachzuvollziehen.
project/
expense_app/
__init__.py
math_tools.py
__main__.py
Die drei Dateien befinden sich im Paketordner, die Befehle starten Sie jedoch im übergeordneten Ordner project. math_tools.py enthält die wiederverwendbare Berechnung. __main__.py enthält den Anwendungseinstieg für die Ausführung des Pakets. Der Paketname im Befehl lautet expense_app, ohne .py und ohne Dateipfad.
Die Aufteilung baut auf der Zwei-Dateien-Übung zu Modulen und import auf. Die Python-Einführung zu Paketen erklärt die Hierarchie.
Alle Dateien speichern und das Paket ausführen
Speichern Sie alle Dateien in UTF-8. expense_app/__init__.py erhält diesen vollständigen Inhalt; die Dokumentationszeichenkette hat keine sichtbare Ausgabe:
"""Expense calculation package."""
Vollständiger Inhalt von expense_app/math_tools.py:
def total(amounts):
return sum(amounts)
Vollständiger Inhalt von expense_app/__main__.py:
from .math_tools import total
def main():
print(total([3500, 8000, 2500]))
if __name__ == "__main__":
main()
Öffnen Sie das Terminal im Ordner project, wählen Sie Ihren Projektinterpreter und führen Sie aus:
python -m expense_app
Das Ergebnis ist 14000. Es werden keine Zusatzpakete installiert und keine Daten heruntergeladen. Unter macOS oder Ubuntu kann außerhalb einer aktivierten Umgebung python3 -m expense_app der passende Aufruf sein. Die Option -m findet das Paket über die Modulsuche und führt dessen __main__.py im Paketkontext aus; siehe die Referenz zur Option -m.
Der Punkt in from .math_tools import total verweist auf das eigene Paket. Er bezeichnet nicht einfach den aktuellen Arbeitsordner. python expense_app/__main__.py startet dagegen eine einzelne Datei ohne den benötigten Elternpaketkontext und scheitert hier am relativen Import. Behalten Sie beim Vergleichen die drei Dateien unverändert und wechseln Sie nur den Aufruf.
__name__ und den Zeitpunkt von Nebenwirkungen verstehen
Beim Ausführen mit -m expense_app erhält der ausgeführte Einstieg den Namen __main__. Beim regulären Import von expense_app.__main__ lautet sein Name dagegen expense_app.__main__. Die Prüfung am Ende ist deshalb nur im Ausführungsfall wahr. Sie ruft dann main() auf; beim Import wird die Funktion definiert, aber nicht durch diese Prüfung gestartet.
Der Name main für die Funktion ist eine Konvention. Die besondere Bedeutung liegt hier im Namen __main__ des Ausführungsumfelds und im Paketdateinamen __main__.py. Die Dokumentation zum Ausführungsumfeld __main__ erläutert beide Verwendungen.
Die Bedingung verhindert nicht die Ausführung sämtlicher Anweisungen auf Modulebene. Der Import von math_tools, die Funktionsdefinition und gegebenenfalls Anweisungen in __init__.py werden weiterhin ausgeführt. Fügen Sie außerhalb der Funktion beispielsweise einen Dateizugriff hinzu, würde dieser auch beim Import stattfinden. Halten Sie initialisierenden Code klein und verschieben Sie die eigentliche Arbeit in ausdrücklich aufgerufene Funktionen.
Modulsuche und Dateisuche bleiben getrennte Aufgaben. -m hilft dem relativen Import, macht aber einen gewöhnlichen relativen Datenpfad nicht automatisch zum Pfad im Paketordner. Ein späterer Aufruf von open("data.csv") würde weiter vom Arbeitsordner abhängen. Verwenden Sie bei solchen Erweiterungen passende ausdrückliche Pfade oder Paketressourcen, anstatt aus dem erfolgreichen Import auf die Lage aller Datendateien zu schließen.
Programmausführung und Wiederverwendung getrennt prüfen
Führen Sie weiterhin aus dem Ordner project diesen Importtest aus:
python -c "import expense_app.__main__; print('imported')"
Es darf nur imported erscheinen. Wird zusätzlich 14000 angezeigt, prüfen Sie, ob main() versehentlich außerhalb der Bedingung aufgerufen wird oder die Berechnung direkt auf Modulebene steht. Stellen Sie die ursprüngliche Einrückung und die Bedingung wieder her und wiederholen Sie sowohl den Importtest als auch python -m expense_app.
- Ändern Sie nur den ersten Betrag in
main()von 3500 zu 4500. Die Paketausführung muss 15000 liefern, der Importtest weiterhin nurimported. Stellen Sie den Betrag danach wieder her. - Starten Sie testweise aus dem Paketordner selbst
python -m expense_app. In dieser noch nicht installierten einfachen Struktur liegt der gesuchte Paketordner nicht darunter und die Suche kann scheitern. Wechseln Sie zurück zum Projektordner, ohne den Quellcode oder globale Suchpfade zu ändern. - Vergleichen Sie die direkte Dateiausführung mit der Paketausführung aus
project. Der erwartete relative Importfehler der ersten Variante wird durch die passende Startmethode behoben, nicht durch das Entfernen des relativen Imports auf Verdacht.
Wenn Sie den Paketordner umbenennen, müssen die entsprechenden Importpfade und Startbefehle mit angepasst werden. Ein Paket in einem Quellordner ist außerdem noch keine veröffentlichte, installierbare Distribution: Dafür kommen unter anderem Projektmetadaten, ein Build und eine Installation in einer frischen Umgebung hinzu. Für diese Übung genügt die korrekt aufgebaute und getrennt auf Ausführung und Import geprüfte Struktur.




