Module

Module werden in Python verwendet, um größere Projekte zu organisieren. Die Python-Standardbibliothek ist in Module aufgeteilt, um sie überschaubarer zu machen. Ihr müsst euren eigenen Code zwar nicht in Modulen organisieren, aber wenn ihr umfangreichere Programme schreibt, oder Code, den ihr wiederverwenden möchtet, solltet ihr dies tun.

Was ist ein Modul?

Ein Modul ist eine Datei, die Code enthält. Sie definiert eine Gruppe von Python-Funktionen oder anderen Objekten, und der Name des Moduls wird vom Namen der Datei abgeleitet.

Module enthalten meist Python-Quellcode [1], fassen verwandte Python-Objekte zusammen und helfen, Namenskonflikte zu vermeiden. So könnt ihr für euer Programm ein Modul namens mymodule schreiben, das eine Funktion namens my_func() definiert. Im selben Programm möchtet ihr vielleicht auch ein anderes Modul namens othermodule verwenden, das ebenfalls eine Funktion namens my_func() definiert, aber etwas anderes tut als mymodule.my_func(). Ohne Module wäre es unmöglich, zwei verschiedene Funktionen mit demselben Namen zu verwenden. Mit Modulen könnt ihr in eurem Hauptprogramm auf die Funktionen mymodule.my_func() und othermodule.my_func() verweisen. Jedes Modul erstellt automatisch einen Namensraum, wodurch Variablen mit demselben Namen in verschiedenen Modulen niemals versehentlich kollidieren: die beiden my_func()-Funktionen können nicht verwechselt werden, wenn ihr in Python sog. Namensräume verwendet. Ein Namensraum ist im Wesentlichen ein Dictionary mit Bezeichnungen für die dort zur Verfügung stehenden Funktionen, Klassen, Module usw.

Namespaces are one honking great idea – let’s do more of those!

– The Zen of Python, von Tim Peters

>>> import cmath, math
>>> math.sin(34)
0.5290826861200238
>>> cmath.sin(34)
(0.5290826861200238-0j)

Warnung

Ein Import mit Platzhaltern macht diese Trennung der Namensräume jedoch zunichte: import * überträgt jeden Namen aus einem Modul in ein anderes.

Module werden auch verwendet, um Python selbst überschaubarer zu machen. Die meisten Standardfunktionen von Python sind nicht in den Kern der Sprache integriert, sondern werden über spezielle Module bereitgestellt, die ihr bei Bedarf laden könnt.

Added in version 3.15: Python 3.15 bringt mit PEP 810 explicit lazy imports: Wenn einer import-Anweisung das Schlüsselwort lazy vorangestellt wird, wird das Modul nicht sofort geladen, sondern stattdessen ein Proxy-Objekt erstellt. Das eigentliche Modul wird bei der ersten Verwendung geladen:

>>> import sys
>>> lazy import json
>>> lazy from pathlib import Path
>>> "json" in sys.modules
False
>>> with Path.open("books.json") as f:
...     data = json.load(f)
...
>>> "json" in sys.modules
True

Wenn ihr lazy nicht direkt verwenden könnt, z. B. in älteren Python-Versionen, kann ein __lazy_modules__-Modul als Container definiert werden, dessen Importanweisungen lazy behandelt werden:

>>> __lazy_modules__ = {"json"}
>>> import json
>>> import sys

Python 3.14 und früher ignorieren __lazy_modules__, während 3.15 und höher die lazy “ Variante verwenden.

Siehe auch

Erstellen von Modulen

Vermutlich der beste Weg, um etwas über Module zu lernen, ist das Erstellen eines eigenen Moduls. Hierzu erstellen wir eine Textdatei mit dem Namen wc.py, und geben in diese Textdatei den unten stehenden Python-Code ein. Wenn ihr IDLE verwendet, wählt File ‣ New Window und beginnt mit der Eingabe.

Es ist einfach, eigene Module zu erstellen, die auf die gleiche Weise importiert und verwendet werden können wie die in Python eingebauten Bibliotheksmodule. Das folgende Beispiel ist ein einfaches Modul mit einer Funktion, die zur Eingabe eines Dateinamens auffordert und die Anzahl der in dieser Datei vorkommenden Wörter ermittelt.

 1"""wc module. Contains function: words_occur()"""
 2
 3
 4def words_occur():
 5    """words_occur() - count the occurrences of words in a file."""
 6    # Prompt user for the name of the file to use.
 7    file_name = input("Enter the name of the file: ")
 8    # Open the file, read it and store its words in a list.
 9    with open(file_name, "r") as f:
10        word_list = f.read().split()
11    # Count the number of occurrences of each word in the file.
12    occurs_dict = {}
13    for word in word_list:
14        # increment the occurrences count for this word
15        occurs_dict[word] = occurs_dict.get(word, 0) + 1
16    # Print out the results.
17    print(
18        f"File {file_name} has {len(word_list)} words, "
19        f"{len(occurs_dict)} are unique:"
20    )
21    print(occurs_dict)
22
23
24if __name__ == "__main__":
25    words_occur()
Zeilen 1 und 5

Docstrings sind Standardmethoden zur Dokumentation von Modulen, Funktionen, Methoden und Klassen.

Zeile 10

read gibt eine Zeichenkette zurück, die alle Zeichen in einer Datei enthält, und split gibt eine Liste der Wörter einer Zeichenkette zurück, die anhand von Leerzeichen aufgespalten wurde.

Zeilen 25 und 26

Mit dieser if-Anweisung könnt ihr das Programm auf zweierlei Arten nutzen:

  • zum Importieren in der Python-Shell oder einem anderen Python-Skript ist __name__ der Dateiname:

    >>> import wc
    >>> wc.words_occur()
    Enter the name of the file: README.rst
    File README.rst has 332 words (191 are unique)
    {'Schnelleinstieg': 1, ...}
    

    Alternativ könnt ihr auch words_occur direkt importieren:

    >>> from wc import words_occur
    >>> words_occur()
    Enter the name of the file: README.rst
    File README.rst has 332 words (191 are unique)
    {'Schnelleinstieg': 1, ...}
    

    Ihr könnt den interaktiven Modus der Python-Shell oder von IDLE verwenden, um ein Modul während der Erstellung inkrementell zu testen. Wenn ihr jedoch euer Modul auf der Festplatte ändert, wird es durch die erneute Eingabe des Import-Befehls nicht erneut geladen. Zu diesem Zweck müsst ihr die Funktion reload aus dem importlib-Modul verwenden:

    >>> import wc, importlib
    >>> importlib.reload(wc)
    <module 'wc' from '/home/veit/.local/lib/python3.14/site-packages/wc.py'>
    
  • als Skript wird es mit dem Namen __main__ ausgeführt und die Funktion words_occur() aufgerufen:

    $ python3 wc.py
    Enter the name of the file: README.rst
    File README.rst has 332 words (191 are unique)
    {'Schnelleinstieg': 1, ...}
    

Speichert diesen Code zunächst in einem der Verzeichnisse des Modul-Suchpfads, die in der Liste von sys.path zu finden ist. Als Ende des Dateinamens empfiehlt sich .py, da hierdurch die Datei als Python-Quellcode ausgewiesen wird.

Bemerkung

Die Liste von Verzeichnissen, die mit sys.path angezeigt wird, hängt von eurer Systemkonfiguration ab. Diese Liste von Verzeichnissen wird von Python in der Reihenfolge durchsucht, wenn eine Import-Anweisung ausgeführt wird. Das erste gefundene Modul, das die Importanforderung erfüllt, wird verwendet. Wenn es kein zutreffendes Modul in diesem Suchpfad gibt, wird ein ImportError ausgelöst.

Wenn ihr IDLE verwendet, könnt ihr euch den Suchpfad und die darin enthaltenen Module grafisch ansehen, indem ihr das Fenster File ‣ Path Browser verwendet.

Die Variable sys.path wird mit dem Wert der Umgebungsvariablen PYTHONPATH initialisiert, falls diese existiert. Wenn ihr ein Python-Skript ausführt, wird in die sys.path-Variable für dieses Skript das Verzeichnis, in dem sich das Skript befindet, als erstes Element eingefügt, so dass ihr auf bequeme Weise feststellen könnt, wo sich das ausführende Python-Programm befindet.

Befehlszeilenargumente

Wollt ihr in unserem Beispiel den Dateinamen als Befehlszeilenargument übergeben, also mit

$ python3 wc.py README.rst

so könnt ihr dies einfach mit folgender Änderung unseres Scripts:

--- /home/docs/checkouts/readthedocs.org/user_builds/python-basics-tutorial-de/checkouts/latest/docs/modules/wc.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/python-basics-tutorial-de/checkouts/latest/docs/modules/wcargv.py
@@ -1,10 +1,12 @@
 """wc module. Contains function: words_occur()"""
+
+import sys
 
 
 def words_occur():
     """words_occur() - count the occurrences of words in a file."""
     # Prompt user for the name of the file to use.
-    file_name = input("Enter the name of the file: ")
+    file_name = sys.argv.pop()
     # Open the file, read it and store its words in a list.
     with open(file_name, "r") as f:
         word_list = f.read().split()
sys.argv

gibt eine Liste der Befehlszeilenargumente zurück, die an ein Python-Skript übergeben wurden. argv[0] ist der Skriptname.

.pop

entfernt das Element an der angegebenen Position in der Liste und gibt es zurück. Wenn kein Index angegeben wird, entfernt .pop() das letzte Element in der Liste und gibt es zurück.

Das argparse-Modul

Ihr könnt ein Skript so konfigurieren, dass es sowohl Kommandozeilenoptionen als auch Argumente akzeptiert. Das argparse-Modul unterstützt beim Parsen verschiedener Argumenttypen und kann sogar Nachrichten erzeugen. Um das argparse-Modul zu verwenden, erstellt eine Instanz von ArgumentParser, füllt sie mit Argumenten und lest dann sowohl die optionalen als auch die Positionsargumente. Das folgende Beispiel veranschaulicht die Verwendung des Moduls:

--- /home/docs/checkouts/readthedocs.org/user_builds/python-basics-tutorial-de/checkouts/latest/docs/modules/wc.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/python-basics-tutorial-de/checkouts/latest/docs/modules/wcargparse.py
@@ -1,10 +1,15 @@
 """wc module. Contains function: words_occur()"""
+
+from argparse import ArgumentParser
 
 
 def words_occur():
     """words_occur() - count the occurrences of words in a file."""
+    parser = ArgumentParser()
     # Prompt user for the name of the file to use.
-    file_name = input("Enter the name of the file: ")
+    parser.add_argument("-f", "--file", dest="filename", help="read data from the file")
+    args = parser.parse_args()
+    file_name = args.filename
     # Open the file, read it and store its words in a list.
     with open(file_name, "r") as f:
         word_list = f.read().split()

Dieser Code erzeugt eine Instanz von ArgumentParser und fügt dann das Argument filename hinzu. Das argparse-Modul gibt ein Namespace-Objekt zurück, das die Argumente als Attribute enthält. Ihr könnt die Werte der Argumente mit der Punktnotation abrufen, in unserem Fall mit args.filename.

Ihr könnt das Skript nun aufrufen mit:

$ python3 wcargparse.py -f index.rst

Zudem wird automatisch eine Hilfeoption -h oder --help erzeugt:

$ python3 wcargparse.py -h
usage: wcargparse.py [-h] [-f FILENAME]

optional arguments:
  -h, --help            show this help message and exit
  -f FILENAME, --file FILENAME
                        read data from the file

Öffentliche API mit __all__ deklarieren

Mit __all__ könnt ihr eine öffentliche API eures Moduls bereitstellen, wodurch bestimmt wird, was mit from MODUL import * importiert wird. Angenommen, euer Modul sieht folgendermaßen aus:

perimeter.py
from math import sqrt

__all__ = ["circle_perimeter", "square_length", "square_perimeter"]
pi = 3.141592653589793


def circle_perimeter(diameter):
    return diameter * pi


def square_perimeter(length):
    return length * 4


def square_length(area):
    return sqrt(area)

Dann könnt ihr dieses Modul folgendermaßen verwenden:

>>> from perimeter import *
>>> circle_perimeter(3)
9.42477796076938
>>> square_perimeter(3)
12
>>> square_length(16)
4.0

Ihr könnt jedoch nicht die pi-Variable aus dem Modul verwenden:

>>> pi
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    pi
NameError: name 'pi' is not defined

Auch mit dir() erhaltet ihr zusätzlich nur die beiden Funktionen aus __all__:

>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'circle_perimeter', 'square_length', 'square_perimeter']

Dies hat den Vorteil, dass Interne Hilfsfunktionen und -Variablen gekapselt und nicht versehentlich exportiert werden. Ohne __all__ wäre jeder Name, der nicht mit einem Unterstrich beginnt, ebenfalls importiert worden, einschließlich pi und der sqrt-Funktion, die wir aus math importiert haben.

Die Hilfefunktion help() liest ebenfalls __all__ aus und dokumentiert nicht alles im Modul; die interne Variable pi und sqrt, das nicht einmal von uns stammt, werden nicht angezeigt:

>>> import perimeter
>>> help(perimeter)
Help on module perimeter:

NAME
    perimeter

FUNCTIONS
    circle_perimeter(diameter)

    square_length(area)

    square_perimeter(length)

DATA
    __all__ = ['circle_perimeter', 'square_length', 'square_perimeter']

Checks

  • Wenn ihr ein Modul my_math erstellt habt, das eine Funktion divide() enthält, welche Möglichkeiten gibt es, diese Funktion zu importieren und dann zu verwenden? Was sind die Vor- und Nachteile der einzelnen Möglichkeiten?

  • Eine Variable min ist im Modul scope.py enthalten. In welchem der folgenden Kontexte kann min verwendet werden?

    1. Mit dem Modul selbst

    2. Innerhalb der Funktion scope() des Moduls

    3. Innerhalb eines Skripts, das das Modul scope.py importiert hat

  • Packt die Funktionen, die ihr am Ende von Dekoratoren erstellt haben, als eigenständiges Modul. Dabei soll die Funktionen zunächst lediglich von einem anderen Skript aus vollständig nutzbar sein.

  • Macht euer Modul ausführbar.

  • Schreibt eure Version des wc-Dienstprogramms so um, dass es sowohl die Unterscheidung zwischen Bytes und Zeichen als auch die Möglichkeit, aus Dateien und von der Standardeingabe zu lesen, implementiert.