Module und Importe
Eine Datei, die du geschrieben hast, aus einer anderen Datei verwenden, was ein Import tatsächlich macht, warum `if __name__ == "__main__"` nötig ist, die Standardbibliothek und das Verbergen eines Moduls hinter einer eigenen Datei.
Wie möchtest du lernen?
Das Problem, das wir lösen
Im letzten Kapitel haben wir eine Funktion line_total geschrieben. Jetzt braucht ein zweites Programm dasselbe.
Der einfache Weg ist, sie zu kopieren. Am ersten Tag funktioniert das.
Dann ändert sich der Steuersatz. Du korrigierst eine Datei und vergisst die andere. Jetzt zeigen zwei Programme zwei verschiedene Summen für dieselbe Bestellung, und es gibt keine Möglichkeit zu sagen, welche richtig ist — denn beide sind richtig, laut ihrer eigenen Datei.
Das Problem ist nicht der Aufwand des Kopierens. Das Problem ist, dass die Wahrheit jetzt an zwei Orten lebt.
import pricing
print(pricing.TAX_RATE)
print(pricing.line_total(15.0, 3))0.15
51.75Eine Datei, und beliebig viele Programme nutzen sie. Wenn sich der Steuersatz ändert, ändert er sich an einer einzigen Stelle.
Dieses Kapitel dreht sich um genau diese eine Zeile — import — und darum, was dahinter tatsächlich passiert.
Am Ende dieses Kapitels kannst du
- Eine Datei, die du geschrieben hast, aus einer anderen Datei importieren
- Sagen, was
import x,from x import yundimport x as zunterscheidet - Sagen, was im Moment eines Imports passiert, und wie oft das geschieht
- Erklären, warum
if __name__ == "__main__":geschrieben wird - Module aus der Standardbibliothek einbinden
- Wissen, wo Python sucht, und vermeiden, ein Modul hinter einer eigenen Datei zu verstecken
Voraussetzungen: Gültigkeitsbereich und veränderliche Standardwerte.
Ein Modul ist einfach eine Datei
Eine Datei namens pricing.py ist ein Modul namens pricing. Nichts muss deklariert werden, und es muss keine besondere Zeile geschrieben werden — die Datei zu haben reicht.
import pricing bringt das ganze Modul herein, und auf die Dinge darin greift man über einen Punkt zu: pricing.TAX_RATE, pricing.line_total(...).
Der Punkt wirkt wie zusätzliches Tippen, aber er hat eine Aufgabe — beim Lesen zeigt er, woher das Ding kommt. In einer großen Datei hilft das sehr.
Nur das importieren, was du brauchst
from pricing import line_total
print(line_total(15.0, 3))
print(TAX_RATE)51.75
NameError: name 'TAX_RATE' is not definedfrom ... import ... bringt nur den Namen herein, der angefordert wurde, und dieser Name wird dann direkt benutzt — ohne Punkt.
(Vor der Fehlerzeile druckt Python auch einige Traceback-Zeilen; in diesem Kapitel wird meist nur die letzte Zeile gezeigt.)
Die anderen Namen kommen aber nicht mit. TAX_RATE wurde nicht angefordert, also ist es nicht da, obwohl es in derselben Datei steht.
Welche Form soll man nehmen? Grob gesagt: from, wenn du zwei oder drei Namen brauchst, das ganze Modul, wenn du viele brauchst. Und eine Sache sollte man nie tun — from pricing import *. Es holt stillschweigend jeden Namen herein, und danach gibt es keine Möglichkeit mehr herauszufinden, woher line_total kommt.
Beim Hereinholen umbenennen
import pricing as p
print(p.line_total(15.0, 3))51.75as gibt dem Modul innerhalb dieser Datei einen kürzeren Namen.
Schreib es, wenn der Name wirklich lang ist oder es eine übliche Konvention dafür gibt. Ein Ein-Buchstaben-Name aus Bequemlichkeit nimmt die Lesbarkeit wieder weg, die der Punkt bietet — niemand, der p.line_total(...) liest, kann erkennen, woher es kommt.
Import bedeutet, die Datei auszuführen
Das ist der Teil, den die meisten erst später entdecken, meist im ungünstigen Moment.
tools.py:
print("tools is being set up")
def ping():
return "pong"main.py:
print("main starts")
import tools
import tools
print(tools.ping())
print("main ends")main starts
tools is being set up
pong
main endsZwei Dinge sind zu beachten.
Import bedeutet, die Datei auszuführen, von oben nach unten. Die def-Zeilen erstellen Funktionen, und alles andere — etwa dieses print — passiert tatsächlich. Steht ein print oder ein input auf oberster Ebene eines Moduls, läuft es bei jedem mit, der es importiert.
Das zweite import tools hat nichts getan. Ein Modul läuft einmal pro Programm; danach gibt Python das bereits geladene Modul zurück. Dasselbe Modul an zehn Stellen zu importieren ist also sicher und günstig.
__name__ und diese berühmte Zeile
Jedes Modul hat darin einen Namen __name__, den Python selbst setzt.
tools.py:
def ping():
return "pong"
print("module name is", __name__)
if __name__ == "__main__":
print("running directly")Als python tools.py ausgeführt:
module name is __main__
running directlyAber import tools aus einer anderen Datei:
module name is toolsDie Zeile running directly erschien diesmal nicht, weil die Bedingung falsch war.
Die Regel ist einfach: die Datei, die du ausführst, hat __name__ gleich "__main__"; importierte Dateien haben ihren eigenen Namen.
Also läuft alles innerhalb von if __name__ == "__main__": nur, wenn die Datei direkt ausgeführt wird, nie wenn sie importiert wird.
Warum das nötig ist, folgt aus dem vorherigen Abschnitt. Import bedeutet, die Datei auszuführen — wenn also main() am Ende einer Datei steht, startet jeder, der sie importiert, plötzlich auch dein Programm. Diese Zeile verschließt die Tür.
Die Standardbibliothek
Sehr viele Module werden mit Python selbst ausgeliefert. Nichts muss installiert werden.
import math
import statistics
print(math.sqrt(144))
print(math.floor(3.7), math.ceil(3.2))
print(statistics.mean([10, 20, 60]))12.0
3 4
30Ein weiteres, das häufig vorkommt:
import random
random.seed(7)
print(random.randint(1, 100))
print(random.choice(["pen", "bag", "ink"]))42
penOhne random.seed(7) würde jeder Lauf andere Zahlen liefern — was ja genau der Sinn ist. Ein Seed (Startwert) legt die Folge fest, sodass die Ausgabe oben auf derselben Python-Version auf deinem Rechner gleich sein wird. Beim Schreiben von Tests ist das sehr wichtig: um etwas Zufälliges zu testen, musst du es zuerst wiederholbar machen.
Das ist nicht „zufällig“, sondern „zufällig aussehend“.randomerzeugt Zahlen aus einer Berechnung, also ergibt derselbe Seed immer dieselbe Folge. Verwende es nicht für Passwörter oder Tokens — dafür gibt es das Modulsecrets.
Wo Python sucht
Für import pricing sucht Python in dieser Reihenfolge: zuerst die Module, die in den Interpreter eingebaut sind, dann den Ordner des gerade ausgeführten Skripts, dann die Standardbibliothek, dann installierte Pakete.
Diese Reihenfolge hat eine Folge, über die viele stolpern.
Es gibt eine Datei namens random.py in deinem Ordner, und du schreibst:
import random
print(random.choice([1, 2, 3]))AttributeError: module 'random' has no attribute 'choice'Deine Datei kam vor dem echten random, also wurde deine importiert — und darin steht kein choice.
Die Meldung ist verwirrend, weil sie sagt, random habe kein choice, was unmöglich klingt. Aber sie spricht von deinem random.
Die Regel: benenne eine Datei nie nach einem Modul. random.py, string.py, json.py, csv.py — sie wirken wie harmlose Namen, und genau deshalb funktioniert die Falle.
Eine Ausnahme, die die Regel erklärt. Auf vielen Installationen (zum Beispiel dem Linux-System, auf dem die Beispiele dieses Kapitels ausgeführt wurden) gibtimport mathauch dann das echte Modul, wenn du eine Dateimath.pyerstellst, nicht deine. Denn dort istmathin den Interpreter eingebaut, und eingebaute Module kommen in der Suchreihenfolge zuerst. Welche Module eingebaut sind, hängt davon ab, wie Python gebaut wurde. Die Falle schnappt also bei manchen Namen zu und bei anderen nicht — was schwerer zu merken ist, als die Datei einfach umzubenennen.
Dateien in Ordnern anordnen
Wenn Dateien sich vermehren, lassen sie sich in Ordnern gruppieren. Lege eine Datei __init__.py in den Ordner — leer reicht — und der Ordner wird zu einem Paket.
shop/
__init__.py
pricing.py
main.pyDer Pfad kann dann mit Punkten geschrieben werden:
from shop.pricing import line_total
print(line_total(15.0, 3))51.75Das reicht für jetzt. Für den Rest dieses Kapitels bleiben wir bei flachen Dateien.
Ein vollständiges Beispiel
Drei Dateien, bei denen die Abhängigkeiten nur in eine Richtung zeigen.
pricing.py:
"""Prices and tax. Nothing here prints, and nothing here asks for input."""
TAX_RATE = 0.15
def line_total(price, quantity):
"""One line of an order, tax included."""
return round(price * quantity * (1 + TAX_RATE), 2)
def order_total(lines):
"""`lines` is a list of (name, price, quantity) tuples."""
return round(sum(line_total(price, qty) for _, price, qty in lines), 2)report.py:
"""Turning numbers into lines of text. It imports pricing; pricing imports nothing."""
import pricing
def receipt(lines):
rows = [f"{name:<8} {pricing.line_total(price, qty):>8.2f}"
for name, price, qty in lines]
rows.append("-" * 17)
rows.append(f"{'total':<8} {pricing.order_total(lines):>8.2f}")
return rows
if __name__ == "__main__":
print("report.py has no data of its own to show")main.py:
"""The only file that is meant to be run."""
import statistics
from report import receipt
ORDER = [
("pen", 15.0, 3),
("bag", 850.0, 1),
("ink", 120.0, 2),
]
def main():
for row in receipt(ORDER):
print(row)
print()
quantities = [qty for _, _, qty in ORDER]
print("mean quantity:", statistics.mean(quantities))
if __name__ == "__main__":
main()Als python main.py ausgeführt:
pen 51.75
bag 977.50
ink 276.00
-----------------
total 1305.25
mean quantity: 2Vier Dinge lohnt es sich anzusehen.
Die Abhängigkeiten laufen in eine Richtung. main kennt report, report kennt pricing, und pricing kennt niemanden. Das ergibt eine Kette, und die Datei am Ende der Kette — pricing — lässt sich vollständig für sich allein testen. Hätte pricing report zurückimportieren müssen, könnte keiner von beiden mehr allein stehen.
pricing.py gibt nichts aus und fragt nichts ab. Es nimmt Werte und gibt Werte zurück — die reinen Funktionen aus Kapitel einundzwanzig. Da Import bedeutet, die Datei auszuführen, ist diese Zurückhaltung keine Höflichkeit, sondern eine Voraussetzung.
report.py hat ebenfalls ein if __name__ == "__main__":, und es tut nichts Nützliches — die Datei hat keine eigenen Daten, also gibt es nichts anzuzeigen. Man könnte es weglassen. Es ist da, damit die Datei, falls sie je direkt ausgeführt wird, wenigstens etwas sagt, statt schweigend nichts zu tun.
Außer main.py tut keine Datei von selbst etwas. Das ganze Programm hat genau einen Einstiegspunkt, und wenn du ihn liest, weißt du, was passieren wird.
Wenn etwas schiefgeht
ModuleNotFoundError: No module named 'priceing' Ein Tippfehler, oder die Datei liegt in einem anderen Ordner. Prüf, ob sie neben dem Skript liegt, das du ausführst.
ImportError: cannot import name 'line_totl' from 'pricing' Das Modul wurde gefunden, der Name darin nicht. Die Meldung endet mit dem vollständigen Dateipfad des Moduls in Klammern und schlägt oft den ähnlichen Namen vor — Did you mean: 'line_total'?
AttributeError: module 'random' has no attribute 'choice' Fast immer verdeckt eine eigene Datei das echte Modul. Such in deinem Ordner nach diesem Namen und benenne die Datei um. Das reicht — sobald die .py-Datei weg ist, verwendet Python die alte Kopie in __pycache__ nicht mehr.
ImportError: cannot import name 'rows' from partially initialized module 'table' (most likely due to a circular import) Zwei Dateien importieren einander, und mindestens eine von ihnen fordert mit from ... import ... einen Namen an, der in der anderen noch nicht existiert — weil diese Datei selbst noch nicht von oben nach unten fertig gelaufen ist. Die übliche Lösung: Verschieb, was beide brauchen, in eine dritte Datei, die keine von beiden importiert.
Beim Import wurde etwas ausgegeben oder eine Eingabe verlangt Auf oberster Ebene des Moduls steht Code. Verschieb ihn in eine Funktion und rufe sie innerhalb von if __name__ == "__main__": auf.
Ich habe die Datei geändert, sehe aber immer noch das alte Verhalten Führ das Programm erneut aus — ein laufendes Programm liest das Modul nicht neu ein. In einem Notebook startest du den Kernel neu.
Schritt 4 von 6 — Vorhersagen
Prüfe dein Verständnis
line_total wurde angefordert, TAX_RATE nicht. Was passiert?
# pricing.py
TAX_RATE = 0.15
def line_total(price, quantity):
return round(price * quantity * (1 + TAX_RATE), 2)
# main.py
from pricing import line_total
print(line_total(15.0, 3))
print(TAX_RATE)- A`51.75` wird gedruckt, dann `None`
- BSowohl `51.75` als auch `0.15` werden gedruckt
- C`51.75` wird gedruckt, dann ein `NameError`
- DEin `ImportError`
import tools steht zweimal da. Was wird gedruckt?
# tools.py
print("tools is being set up")
def ping():
return "pong"
# main.py
print("main starts")
import tools
import tools
print(tools.ping())
print("main ends")- Atools is being set up main starts pong main ends
- Bmain starts pong main ends
- Cmain starts tools is being set up pong main ends
- Dmain starts tools is being set up tools is being set up pong main ends
python main.py wird ausgeführt. Was wird gedruckt?
# tools.py
print("name:", __name__)
if __name__ == "__main__":
print("direct")
# main.py
import tools
print("done")- Aname: tools done
- Bname: main done
- Cname: __main__ direct done
- Dname: tools direct done
Zum Antworten brauchst du ein Konto
Melde dich an, um deine Antworten zu prüfen
Die Fragen stehen oben, und sie im Kopf durchzuarbeiten ist der wichtige Teil. Melde dich an, um die Antworten, die Erklärungen und die dreistufigen Hinweise zu sehen.
Jetzt bist du dran
Erstelle drei Dateien in einem Ordner.
textutil.py — zwei Funktionen: initials(name), das "rafi ahmed" in "RA" verwandelt, und shorten(text, limit), das langen Text kürzt und am Ende ... anfügt. Kein print in dieser Datei.
table.py — importiert textutil und bietet rows(people), das eine Liste von Namen entgegennimmt und eine Liste formatierter Zeilen zurückgibt.
main.py — importiert table, ruft es mit einigen Namen auf und gibt aus. Schreib in alle drei Dateien if __name__ == "__main__":.
Dann vier Experimente, die es alle wert sind, mit eigenen Augen gesehen zu werden:
- Füge
print("loaded")ganz oben intextutil.pyein und führepython main.pyaus. Erscheint die Zeile? Vor oder nach dem erstenprintvonmain.py? - Schreib
import tablezweimal inmain.py. Wie oft wirdloadedgedruckt? - Erstelle im Ordner eine leere Datei namens
statistics.py, dann importiere inmain.pystatisticsund rufstatistics.mean([1, 2])auf. Lies die Meldung — von welchemstatisticsist die Rede? - Füge ganz oben in
textutil.pyfrom table import rowshinzu, sodass sich die beiden gegenseitig importieren, und führe dannpython main.pyaus. Die letzte Zeile der Meldung beginnt so:ImportError: cannot import name 'rows' from partially initialized module 'table'. (Ein einfachesimport tablegibt keinen Fehler: dann bekommttextutilnur einen Namen für das halb aufgebaute Modul, undrowswird erst später nachgeschlagen, wenn eine Funktion läuft.from table import rowsfordert den Namen sofort an, währendtable.pynoch in der ersten Zeile hängt.)
Die letzten zwei sind Fehler, die Stunden kosten können, wenn die Meldung nicht vertraut ist, und Sekunden, sobald du sie selbst verursacht hast.
Schritt 6 von 6
Herausforderung — das Kapitel-Quiz
Zehn Fragen von leicht bis schwer. Die letzten sind absichtlich knifflig.
Melde dich an, um das Quiz zu machen