Ein Programm über mehrere Dateien organisieren · Übung
Importstile und Code beim Import
Ein Import macht mehr, als einen Namen bereitzustellen. Er führt die Datei aus, von oben nach unten, einmal.
Dieser Satz erklärt die meisten überraschenden Importeffekte, denen Einsteiger begegnen. Es lohnt sich, das zu beobachten.
Was ein Import tatsächlich tut
Angenommen, noisy.py enthält Folgendes:
print("noisy.py is being imported")
GREETING = "hello"
def shout():
return GREETING.upper()
Der Import erzeugt eine Ausgabe. Nicht beim Aufruf von shout, sondern im Moment des Imports.
import noisy # prints "noisy.py is being imported"
print(noisy.shout()) # prints "HELLO"
Jede Zeile auf oberster Ebene läuft. print gibt aus, GREETING = "hello" weist zu, und die def-Anweisung erzeugt eine Funktion. Eine Funktion zu definieren geschieht selbst beim Import. Ihr Aufruf nicht.
Einmal pro Programmlauf
Wird dasselbe Modul innerhalb eines Programmlaufs zweimal importiert, läuft sein Code normalerweise einmal:
import noisy # prints
import noisy # prints nothing
Python merkt sich bereits importierte Module und gibt dasselbe Modulobjekt zurück. Deshalb können drei Dateien jeweils import models verwenden, ohne dass drei Kopien von Question existieren. Darum gilt main.Question is models.Question.
In einem Programmlauf importieren sowohl main.py als auch report.py das Modul models auf gewöhnliche Weise. Wie oft wird models.py ausgeführt?
Warum das für deine Module wichtig ist
Ein Modul, das beim Import Arbeit erledigt, zwingt diese Arbeit jedem auf, der es importiert, gewünscht oder nicht.
# scoring.py, written carelessly
def percentage(score, total):
...
# a quick demo the author left in
print(percentage(4, 10))
Importiere scoring von irgendwo, und es gibt etwas aus. Deine Testsammlung gibt aus. Eine Webanfrage gibt aus. Es gibt keinen stillen Weg zu percentage, denn die Demo ist Teil der Datei statt Teil eines Programms.
Die Lösung ist die Schutzabfrage, die du seit Kapitel 12 von Grundlagen I kennst:
def percentage(score, total):
...
if __name__ == "__main__":
print(percentage(4, 10))
__name__ ist eine Variable, die Python in jedem Modul setzt. Wird eine Datei direkt gestartet, lautet sie "__main__". Wird sie importiert, lautet sie wie das Modul selbst, "scoring". Der geschützte Block läuft deshalb beim direkten Start der Datei und bleibt beim Import still.
Das ist der gesamte Mechanismus. Er macht die Datei zu zwei Dingen gleichzeitig: einem von anderen verwendbaren Modul und einem ausführbaren Programm.
Die daraus folgende Regel
Auf oberster Ebene sollte ein Modul Dinge definieren, nicht Dinge tun.
Definitionen, Konstanten und Importe auf oberster Ebene sind in Ordnung, denn sie bestimmen, was das Modul ist. Ausgaben, Eingaben, das Öffnen von Dateien und Funktionsaufrufe gehören in eine Funktion oder hinter die Schutzabfrage.
Auch from models import * verdient eine Erwähnung, denn das ist der weitere Importstil, dem Einsteiger begegnen. Er holt alle öffentlichen Namen eines Moduls in die aktuelle Datei. Beim Lesen ist dann nicht klar, woher ein Name stammt. Zwei Module können Namen still überschreiben, und ein neuer Modulname kann eine Datei beschädigen, die ihn nie erwähnt hat. In einer kurzlebigen REPL-Sitzung kannst du das verwenden. In einer Datei solltest du es lassen.
Die Übung
Du schreibst scoring.py, das Modul zur Umwandlung eines Punktestands in einen Anteil und eine Note. Am Ende steht eine Demo, die derzeit beim Import läuft.
Aufgabe
Erstelle scoring.py, die Datei im Editor dieser Lektion.
Es soll zwei Funktionen definieren und beim Import keine Arbeit ausführen:
percentage(score, total)gibt den Punktestand als Anteil zwischen0.0und1.0zurück. Isttotalnull, gibt es0.0zurück, statt eine Ausnahme auszulösen.grade_letter(fraction)gibt ab0.9ein"A"zurück, ab0.8ein"B", ab0.7ein"C", ab0.6ein"D"und darunter ein"F".
Behalte die Demo am Ende, aber stelle sie hinter if __name__ == "__main__":. So läuft sie beim direkten Start und bleibt still, wenn ein anderes Modul die Datei importiert.
Die Bewertung prüft sowohl den stillen Import als auch die direkt gestartete Demo. Run führt im Projekt weiterhin main.py aus. Die Auswahl von scoring.py ändert daran nichts. Submit prüft die Bewertungsdatei; die nächste Lektion verbindet ihre Funktionen mit der laufenden Anwendung.