Modulare Programmierung und Module¶
Modulare Programmierung¶

Modulare Programmierung zerlegt größere Programme in überschaubare, möglichst klar abgegrenzte Komponenten. In Python ist ein Modul typischerweise eine .py-Datei mit Definitionen und ausführbaren Anweisungen. Module schaffen eigene Namensräume und erleichtern Wiederverwendung, Tests und Wartung.
import math
Das Modul math stellt mathematische Konstanten und Funktionen zur Verfügung, z. B. π (math.pi), die Sinusfunktion (math.sin()) und die Cosinusfunktion (math.cos()). Auf jedes Attribut bzw. jede Funktion kann nur durch Voranstellen von "math." zugegriffen werden:
math.pi
math.sin(math.pi/2)
math.cos(math.pi/2)
math.cos(math.pi)
Mehrere Module können syntaktisch in einer import-Anweisung genannt werden. Für bessere Lesbarkeit empfiehlt es sich in normalem Quellcode jedoch meist, pro Zeile ein Modul zu importieren:
import math
import random
Import-Anweisungen können an beliebiger Stelle im Programm platziert werden, aber es gehört zum guten Stil, sie direkt am Anfang eines Programms zu platzieren.
Wenn nur bestimmte Objekte eines Moduls benötigt werden, können wir nur diese importieren:
from math import sin, pi
Die anderen Objekte, z. B. cos, sind nach diesem Import nicht mehr verfügbar. Wir sind in der Lage, auf sin und pi direkt zuzugreifen, d. h. ohne ihnen das Präfix "math" voranzustellen. Anstatt bestimmte Objekte aus einem Modul explizit zu importieren, ist es auch möglich, alles im Namensraum des importierenden Moduls zu importieren. Dies kann durch die Verwendung eines Sternchens im Import erreicht werden:
from math import *
sin(3.01) + tan(cos(2.1)) + e
e
Die Sternchen-Schreibweise (from modul import *) sollte in normalem Programmcode vermieden werden. Sie macht schwer nachvollziehbar, woher ein Name stammt, und kann bestehende Namen unbemerkt überschreiben. Explizite Modulnamen oder gezielte Importe sind deutlich robuster.
from numpy import *
from math import *
print(sin(3))
sin(3)
Lassen Sie uns das vorherige Beispiel leicht modifizieren, indem wir die Reihenfolge der Importe ändern:
from math import *
from numpy import *
print(sin(3))
sin(3)
Sternimporte wirken bequem, weil Namen ohne Modulpräfix verwendet werden können. In größerem Code wird dadurch aber die Herkunft von Namen unklar. Ein verbreiteter und besser lesbarer Weg, Tipparbeit zu sparen, sind Modul-Aliase. Bei NumPy ist beispielsweise folgende Konvention üblich:
import numpy as np
Damit bleibt der Namensraum sichtbar und NumPy-Objekte werden mit np. angesprochen:
import numpy as np
np.diag([3, 11, 7, 9])
Module entwerfen und schreiben¶
Ein Python-Modul ist im einfachsten Fall eine Datei mit der Endung .py, die Definitionen und ausführbare Anweisungen enthält. Der Modulname entspricht normalerweise dem Dateinamen ohne .py.
Als Beispiel speichern wir zwei Fibonacci-Funktionen in fibonacci.py:
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
return fib(n - 1) + fib(n - 2)
def ifib(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
Liegt fibonacci.py auf dem Modul-Suchpfad, kann es importiert und über seinen Modul-Namensraum verwendet werden:
import fibonacci
fibonacci.fib(7)
fibonacci.fib(20)
fibonacci.ifib(42)
fibonacci.ifib(1000)
Die rekursive Variante ist für große Argumente absichtlich ungeeignet: Sie berechnet dieselben Teilprobleme immer wieder. Die iterative Variante ist für dieses Beispiel wesentlich effizienter.
Wer einen häufig verwendeten Namen abkürzen möchte, kann ihn lokal binden; meist sind aber ein expliziter Import oder der Modul-Namensraum lesbarer:
fib = fibonacci.ifib
fib(10)
Besser ist es in vielen Fällen, die benötigten Namen gezielt zu importieren oder – besonders bei großen Bibliotheken – den Modul-Namensraum ausdrücklich beizubehalten.
Mehr über Module¶
Ein Modul darf neben Funktions- und Klassendefinitionen auch ausführbare Top-Level-Anweisungen enthalten. Beim ersten erfolgreichen Import in einer Interpreter-Sitzung wird der Modulcode ausgeführt und das Modul anschließend in sys.modules zwischengespeichert. Weitere normale Imports desselben Modulnamens verwenden dieses bereits geladene Modul.
print("The module is imported now!")
Wir speichern diese eine Anweisung unter dem Namen one_time.py und importieren das Modul:
import one_time
Ein normaler zweiter import one_time führt den Top-Level-Code nicht erneut aus, solange dasselbe Modul bereits in sys.modules geladen ist.
Bei interaktiver Entwicklung kann importlib.reload() ein bereits importiertes Modul erneut ausführen:
from importlib import reload
import one_time
reload(one_time)
reload() ist vor allem ein Werkzeug für interaktive Entwicklung. Bereits mit from modul import name gebundene Namen und schon erzeugte Instanzen werden dadurch nicht automatisch auf neue Definitionen umgebogen. In regulären Programmen sollte die Logik daher nicht von wiederholtem Reloading abhängen.
Jedes Modul besitzt einen eigenen globalen Namensraum. Funktionen, die im Modul definiert sind, verwenden diesen Modul-Namensraum für globale Namen. Auf öffentliche Modulattribute greift man mit Punktnotation wie modul.name zu. Ein Modul kann seinerseits weitere Module importieren; Importe stehen nach üblicher Stilkonvention am Anfang einer Datei.
Direktes Importieren von Namen aus einem Modul¶
Namen können auch direkt in den Namensraum des importierenden Moduls gebunden werden:
from fibonacci import fib, ifib
ifib(500)
Dabei wird der Name fibonacci durch diese from-Anweisung nicht zusätzlich gebunden. Ein Sternimport (from fibonacci import *) importiert grundsätzlich die dafür vorgesehenen öffentlichen Namen, macht deren Herkunft aber schwer nachvollziehbar und sollte in normalem Programmcode vermieden werden:
from fibonacci import *
fib(500)
Module direkt als Skript ausführen¶
Eine Moduldatei kann auch direkt gestartet werden, zum Beispiel:
python fibonacci.py 10
Wird eine Datei direkt ausgeführt, hat die eingebaute Modulvariable __name__ den Wert "__main__". Beim Import als Modul enthält __name__ dagegen den Modulnamen. Damit lässt sich Code formulieren, der nur beim direkten Start ausgeführt wird:
if __name__ == "__main__":
import sys
print(fib(int(sys.argv[1])))
Ein direkter Aufruf kann dann beispielsweise liefern:
$ python fibonacci.py 10
55
Beim normalen import fibonacci wird dieser if-Block nicht ausgeführt. Die Funktionen und anderen Modulobjekte stehen anschließend über den importierten Modul-Namensraum zur Verfügung:
import fibonacci
print(fibonacci.ifib(10))
Modulnamen mit as abkürzen¶
Mit as bindet man das importierte Modul unter einem lokalen Alias:
import math as mathematics
print(mathematics.cos(mathematics.pi))
Die Anweisung import math as mathematics bindet das Modul unter dem Namen mathematics. Sie erzeugt durch diese Importanweisung keine zusätzliche Bindung namens math. Ob math trotzdem existiert, hängt davon ab, ob dieser Name vorher bereits im aktuellen Namensraum gebunden wurde.
Auch einzelne importierte Namen können mit as umbenannt werden.
from math import pi, pow as power, sin as sinus
power(2, 3)
sinus(pi)
Arten von Modulen¶
Es gibt verschiedene Arten von Modulen:
- Solche, die in Python geschrieben sind
Sie haben die Endung: .py - Dynamisch gelinkte C-Module
Die Suffixe sind: .dll, .pyd, .so, .sl, ... - Mit dem Interpreter gelinkte C-Module
Es ist möglich, eine vollständige Liste dieser Module zu erhalten:
import sys
print(sys.builtin_module_names)
Für Built-in-Module wird eine Fehlermeldung zurückgegeben.
Suchpfad für Module¶
Beim Import sucht Python über den Modul-Suchpfad sys.path. Dessen genaue Initialisierung hängt davon ab, wie Python gestartet wurde. Typischerweise enthält er zunächst das Verzeichnis des ausgeführten Skripts beziehungsweise bei einer interaktiven Sitzung das aktuelle Verzeichnis, danach unter anderem Einträge aus PYTHONPATH und installationsabhängige Standardverzeichnisse.
Den tatsächlich verwendeten Suchpfad sollte man deshalb nicht über fest verdrahtete Verzeichnisnamen erklären, sondern direkt abfragen:
import sys
for path in sys.path:
print(path)
Nach einem Import kann module.__file__ bei dateibasierten Modulen anzeigen, aus welcher Datei ein Modul geladen wurde:
import random
print(random.__file__)
Bei eingebauten oder anders bereitgestellten Modulen muss __file__ nicht auf eine normale Quelldatei verweisen beziehungsweise kann fehlen.
import math
math.__file__
Inhalt eines Moduls¶
Mit der eingebauten Funktion dir() und dem Namen des Moduls als Argument, können Sie alle gültigen Attribute und Methoden für dieses Modul auflisten.
import math
dir(math)
Wenn Sie dir() ohne Argument aufrufen, wird eine Liste mit den Namen im aktuellen lokalen Bereich zurückgegeben:
import math
cities = ["New York", "Toronto", "Berlin", "Washington", "Amsterdam", "Hamburg"]
dir()
Es ist möglich, eine Liste der eingebauten Funktionen, Exceptions und anderer Objekte zu erhalten, indem Sie das builtins-Modul importieren:
import builtins
dir(builtins)
Pakete¶
Wenn Sie irgendwann einmal viele Module erstellt haben, verlieren Sie vielleicht den Überblick über diese. Sie haben vielleicht Dutzende oder Hunderte von Modulen und diese können in verschiedene Kategorien eingeteilt werden. Es ist vergleichbar mit der Situation in einem Dateisystem: Anstatt alle Dateien in einem einzigen Verzeichnis zu haben, legt man sie in verschiedenen Verzeichnissen ab, die nach den Themen der Dateien geordnet sind. Wir werden im nächsten Kapitel unseres Python-Tutorials zeigen, wie man Module in Paketen organisiert.

