Funktionen, Module und Pakete in Python

Aus LinuxUser 10/2006

Funktionen, Module und Pakete in Python

Gut verschnürt

Gliedern Sie Ihre Python-Programme in Funktionen, Module und Pakete. Python bietet Ihnen ebenfalls einige davon.

Welcher Sysadmin, welche Programmiererin kennt das nicht? Das bißchen Code, das ein Problem erstmal prima löste, beginnt zu wuchern und dadurch den Blick zu verstellen. Damit die Übersicht nicht verloren geht, ist Aufräumen angesagt: Programmcode, der mehrfach in ähnlicher Form zum Einsatz kommt, gewinnt – ausgegliedert in allgemeinere Funktionen – an Vielseitigkeit und damit Wiederverwendbarkeit. Das verkürzt den Code meist schon um einiges.

Mit einer Sammlung von tausend und einer Funktion ist es aber nicht getan – zusammengehörige Funktionen bündeln Sie in Modulen. Blicken Sie auch hier aufgrund der Anzahl nicht mehr durch, verstauen Sie Modulgruppen in Pakete. Wie Ihnen Python beim Aufräumen hilft, erfahren Sie in diesem Artikel.

Funktionen

Funktionen werden in Python üblicherweise mit dem Schlüsselwort def erstellt:

def addiere(a, b):
    """Gib a plus b zurueck."""
    return a + b
# gibt 5 aus
print addiere(2, 3)

Auf def folgt der Name der zu definierenden Funktion, danach in Klammern die Argumente der Funktion. Dann folgt ein Doppelpunkt. Den darunter folgenden Funktionsrumpf schreiben Sie eingerückt. Alles, was von der ersten Zeile der Funktion mindestens diese Einrücktiefe hat, gehört also noch zur Funktionsdefinition.

Üblicherweise folgt als erster Bestandteil des Funktionsrumpfes eine Zeichenkette, die als Funktionsname.__doc__ abfragbar ist und allgemein Docstring heißt. Dokumentationswerkzeugen wie Pydoc [1] werten diese aus und erstellen daraus einfacher zu lesende Dokumente (Abbildung 1).

Abbildung 1: Von Pydoc [1] generierte HTML-Ausgabe für das Modul <code srcset=

pprint. Die Python-Distribution enthält Pydoc. Hier bildet es die Grundlage für den help-Befehl im interaktiven Interpreter.” width=”300″ height=”231″ /> Abbildung 1: Von Pydoc [1] generierte HTML-Ausgabe für das Modul pprint. Die Python-Distribution enthält Pydoc. Hier bildet es die Grundlage für den help-Befehl im interaktiven Interpreter.

Anders als zum Beispiel in Perl verlangt Python, dass Funktionen Ergebnisse explizit mit der Anweisung return zurückgeben. Folgt auf das Schlüsselwort return kein Ausdruck, ist der Rückgabewert None, ebenso, wenn in der Funktion gar keine return-Anweisung auftaucht.

Besteht der Funktionsrumpf wie oben nur aus einem Ausdruck, dürfen Sie die Funktion auch als anonyme Funktion mit dem Schlüsselwort lambda definieren. Im Ausdruck addiere = lambda a, b: a + b fehlt die return-Anweisung; der nach dem Doppelpunkt stehende Ausdruck ist der Rückgabewert. Der Docstring taucht hier ebenfalls nicht auf, Python setzt ihn implizit auf None.

Vorgabeargumente

Mit der folgenden Syntax definieren Sie Vorgabeargumente (Default-Argumente), die bei fehlenden Argumenten im Funktionsaufruf einspringen:

def addiere(a, b=2):
    """
    Gib a plus b zurueck. Wird b
    weggelassen, ist es 2.
    """
    return a + b
# ergibt 5
print addiere(2, 3)
# ergibt 4
print addiere(2)

In der Funktionsdefinition folgen Argumente mit Vorgabewerten immer nach Argumenten ohne Vorgabewerte. Hier noch ein paar Beispiele zu Vorgabewerten:

def demo(a, b="b", c="c"):
    """Zeige a, b und c."""
    print a, b, c
demo(1, 2, 3)  # 1, 2, 3
demo(1, 2)     # 1, 2, c
demo(1)        # 1, b, c
# Fehler! a hat keinen
# Vorgabewert
demo()

Wenn Sie als Vorgabewert ein veränderliches Objekt verwenden (siehe Teil der Python-Reihe in Heft LinuxUser 09/2006), wie zum Beispiel eine Liste oder ein Dictionary, wertet Python dieses nur bei der Definition der Funktion aus, nicht bei jedem Aufruf:

def sammle(x, L=[]):
    "Sammle x in einer Liste L."
    L.append(x)
    print L
sammle(1)       # [1]
sammle(2)       # [1, 2] !
sammle(3, [5])  # [5, 3]

Wünschen Sie bei Abwesenheit des Arguments tatsächlich jedesmal eine leere Liste als Vorgabe, hilft dieses Idiom:

def immer_neu(x, L=None):
    "Sammle x in einer Liste L."
    if L is None:
        L = []
    L.append(x)
    print L

Benannte Parameter

Sind Ihnen die Namen der Argumente in der Funktionsdefinition geläufig, nicht aber deren Reihenfolge, entlasten Sie einfach Ihr Gedächtnis, indem Sie beim Aufruf der Funktion die Parameternamen mit hinschreiben:

def zeige_a_b(a, b):
    print a, b
zeige_a_b(1, 2)      # 1, 2
zeige_a_b(a=1, b=2)  # 1, 2
zeige_a_b(b=2, a=1)  # 1, 2 !
zeige_a_b(1, b=2)    # 1, 2
# Fehler! Benannte Argumente
# dürfen nicht vor unbenannten
# stehen, nur danach.
zeige_a_b(b=2, 1)

Benannt und unbenannt

Die Syntax *args steht in der def-Anweisung für beliebige unbenannte Argumente (positional arguments, Positionsargumente), die Sie innerhalb der Funktion in einem Tupel finden:

def baue_liste(*T):
    return list(T)
baue_liste(1, 2, 7)  # [1, 2, 7]

Analog sprechen Sie mit **kwargs (keyword arguments, Schlüsselwortargumente) alle benannten Argumente als Dictionary an:

def baue_dictionary( D):
    return D
# {'a': 1, 'b': 2, 'c': 7}
baue_dictionary(a=1, b=2, c=7)
# in Python schon vordefiniert:
dict(a=1, b=2, c=7)
# gibt {'a': 1, 'b': 2, 'c': 7}

Wie bei Dictionaries üblich, gibt es für die Einträge keine garantierte Reihenfolge. Die Kombination von *args und **kwargs ist ebenfalls möglich, wie Listing 1 zeigt.

@#

Listing 1

def zeigs_mir(*T,  D):
    """
    Gib alle Posititions- und Schlüsselwortargumente aus.
    """
    print "Unbenannte Argumente:"
    print "\n".join(T)
    print
    print "Benannte Argumente:"
    for schluessel, wert in sorted(D.items()):
        print "%s: %s" % (schluessel, wert)
zeigs_mir("erst", "unbenannte", "Argumente",
          zahl=7, s="String", liste=[1, 2])

Die Ausgabe sieht dann so aus:

Unbenannte Argumente:
erst
unbenannte
Argumente
Benannte Argumente:
liste: [1, 2]
s: String
zahl: 7

Die Verwendung von *args und **kwargs funktioniert auch im Aufruf:

def zeige(a, b):
    print a, b
zeige(*[1, 2])             # 1, 2
zeige( {'b': 2, 'a': 1})  # 1, 2

Generatorfunktionen

Generatorfunktionen [2] liefern beim Aufruf sogenannte Iteratoren, wie sie vor allem in der Anweisung for zum Einsatz kommen (siehe das Dateibeispiel im letzten LinuxUser). Von gewöhnlichen Funktionen unterscheiden sich Generatorfunktionen dadurch, dass sie das Schlüsselwort yield verwenden. Der Kasten “Suche nach Python” zeigt ein Beispiel.

@#

Suche nach Python

Die folgende Generatorfunktion liefert alle Zeilen aus einer Datei, in denen “Python” vorkommt, ungeachtet der Groß-/Kleinschreibung:

def finde_python(datei):
    for zeile in datei:
        if "python" in zeile.lower():
            yield zeile

Angenommen, die Datei python_zaehlen.txt enthält diesen Text:

Hier testen wir eine Generator-
funktion, die alle Zeilen liefern
soll, die das Wort Python in
beliebiger Groß-/Kleinschreibung
enthalten.
Das heißt, Python, python und
auch die gar seltsame
Schreibweise pYthon sind ok,
nicht jedoch das Wort phython,
das ein weiteres h enthält.

Dann erzeugt der Code

test_datei = open("python_zaehlen.txt")
for zeile in finde_python(test_datei):
    print zeile.rstrip()

die Ausgabe

soll, die das Wort Python in
Das heißt, Python, python und
Schreibweise pYthon sind ok,

Die Generatorfunktion liefert dabei die Zeilen, die den Suchbegriff enthalten, sobald sie gefunden werden. Sie können also eine einfache for-Schleife für die Suche verwenden, ohne zuerst alle Zeilen einzulesen, – was bei der Verarbeitung großer Dateien sehr wichtig ist.

Typische Anwendungsfälle von Generatorfunktionen beziehungsweise Iteratoren sind gleichzeitiges Lesen und Verarbeiten von Dateien, Iteration über nicht-lineare Datenstrukturen (zum Beispiel for knoten in baum: print knoten) oder das Umwandeln von Daten (im Iterator) während Sie über diese iterieren.

Eingebaute Funktionen

Neben vielen Funktionen, die in den Modulen aus der Python-Distribution enthalten sind, gibt es einige so genannte eingebaute Funktionen. Sie können diese Funktionen von allen Stellen im Programm aufrufen, wie Sie das bereits bei len("Hallo") für die Länge eines Strings gesehen haben.

Die Tabelle 1 enthält die meisten eingebauten Funktionen mit einer knappen Beschreibung für den schnellen Überblick. Werte in eckigen Klammern sind optional. Punkte (…) bezeichnen weitere, unter Umständen optionale Argumente. Ausführliche Informationen zu den eingebauten Funktionen finden Sie unter [3].

@#

Einige eingebaute Funktionen

Funktion Beschreibung
Arithmetik
abs(x) Absolutbetrag von x
max() größter Wert
min() kleinster Wert
pow(x, z, ...) Potenzierung
round(x [, n]) x mit n Nachkommastellen
sum(Sequenz) Summe von Sequenz
Attribute, Namensräume
dir([Objekt]) Attributnamen zu Objekt
globals() Objekte im modul-globalen Namensraum
locals() Objekte im lokalen Namensraum
vars([Objekt]) Attribute in Objekt
delattr(Objekt, Name) Attribut Name von Objekt Objekt löschen
getattr(Objekt, Name, Vorgabe) Attribut holen
hasattr(Objekt, Name) prüft, ob Objekt Attribut Name enthält
setattr(Objekt, Name, Wert) Attribut Name von Objekt auf Wert setzen
Datentypen
basestring() Basistyp von str und unicode
bool([Wert]) boolscher Wert (True, False)
complex([Real [, Imagi]]) komplexe Zahl
dict([Mapping oder Sequenz]) Dictionary
file(Name, ...) Dateiobjekt
float([x]) Fließkommazahl
frozenset([Iterabel]) unveränderliche (immutable) Menge (Set)
int([x, [Basis]]) Ganzzahl
list([Sequenz]) Liste
long([x, [Basis]]) Ganzzahl mit beliebig vielen Stellen
object() Basistyp aller Objekte
set([Iterabel]) Menge (Set)
slice() Slice-Objekt
str([Objekt]) Bytestring
tuple([Sequence]) Tupel
type(...) neuen Typ erzeugen
unicode(String) Unicode-codierter String
Objektorientierte Programmierung
classmethod(Funktion) Klassen-, nicht objektspezifische Methode
isinstance(Objekt, Klasse/n) ist Objekt Instanz der (Basis-)Klassen?
issubclass(Klasse, Klasse/n) ist Klasse eine Unterklasse der (Basis-)Klassen?
property(...) Property für Attributzugriff erzeugen
staticmethod(Funktion) Objekt- und klassenunabhängige Methode
super(Typ, ...) “nächste” Basisklasse
Sequenzen
enumerate(Iterierbar) Tupel (Index, Wert) für jede Iteration
iter(Objekt [, Stopwert]) Iterator erzeugen
len(Objekt) Länge von Objekt
range([Start,] Stop [, Schritt]) Sequenz von Start bis Stop
reversed(Sequenz) “umgedrehte” Sequenz
sorted(Iterierbar [, ...]) sortierte Sequenz
xrange([Start,] Stop [, Schritt]) wie range, aber Iterator
Sonstiges
callable(Objekt) ist Objekt aufrufbar?
chr(n) Zeichen mit Code n
cmp(x, y) Vergleichswert für x und y (<0, =0, >0)
help(...) allgemeiner Hilfetext bzw. zu einem Objekt
hex(n) Hexadezimaldarstellung
id(Objekt) eindeutige Id eines Objekts, solange es existiert
oct(n) Oktaldarstellung
open(Name, ...) Datei öffnen und Dateiobjekt liefern
ord(Zeichen) Zeichencode für Zeichen
raw_input([Prompt]) String vom Terminal lesen
reload(Modul) Modul erneut laden
repr(Objekt) kompilierbare Stringform von Objekt
str(Objekt) lesbare Stringform von Objekt
type(Objekt) Typ von Objekt
unichr(n) Unicodezeichen mit Code n

Noch drei Anmerkungen: Viele der Funktionsbeschreibungen sind vermutlich noch rätselhaft, da sie sich auf Themen beziehen, die erst später in dieser Reihe zum Einsatz kommen (zum Beispiel beim objektorientierten Programmieren).

Nicht alle Datentypen haben eine entsprechende eingebaute Funktion. Zum Beispiel ist die Funktion module zum Erzeugen eines Modulobjekts nicht eingebaut, sondern im Modul new enthalten. Und zu guter Letzt sind beim objektorientierten Programmieren in Python die Funktionen isinstance und issubclass normalerweise überflüssig. Sie werden sehen, warum.

Module

Wie Sie Funktionen in Python schreiben, wissen Sie nun. Wächst die Zahl der Funktionen mehr und mehr, bietet es sich an, diese in Modulen zu gruppieren. Ein Modul ist einerseits eine Datei, andererseits – nachdem ein Skript die Datei mit der import-Anweisung lädt – ein Modulobjekt. Die bisher vorgestellten Dateien mit der Endung .py funktionieren auch problemlos als Module, ebenso die Datei fakultaet.py aus Listing 2.

Listing 2

01 #! /usr/bin/python
02 # encoding: iso-8859-1
03
04 def fakultaet(n):
05     """
06     Gib die Fakultät "n!" von n>=0 zurück.
07
08     n! = 1 * 2 * … * (n-1) * n (Beispiel: 4! = 1*2*3*4 = 24)
09     Definitionsgemäß ist 0! = 1.
10     """
11     assert n >= 0, "n darf nicht negativ sein, ist aber %s" % n
12     produkt = 1
13     for i in range(2, n+1):
14         produkt = produkt * i
15     return produkt
16
17 def _test(n, erwartet):
18     """
19     Teste, ob fakultaet(n) gleich dem erwarteten Ergebnis ist.
20     """
21     ergebnis = fakultaet(n)
22     print "fakultaet(%d) berechnet als %d …" % (n, ergebnis),
23     if ergebnis == erwartet:
24         print "ok"
25     else:
26         print "FEHLER!"
27
28 if __name__ == '__main__':
29     _test(0, 1)
30     _test(1, 1)
31     _test(2, 2)
32     _test(9, 362880)

Dieses Modul enthält in den Zeilen 4 bis 15 eine Funktion fakultaet zum Berechnen der so genannten Fakultät einer ganzen Zahl n, dass heißt, dem Produkt aller ganzen Zahlen von 1 bis n. Die assert-Anweisung stellt hier sicher, dass das Argument n nicht negativ ist. (Sie lernen später flexiblere Möglichkeiten zum Abfangen von Fehlern kennen.) Vor dem Eintritt in die for-Schleife (Zeilen 12 und 14) setzt die Funktion den spätere Rückgabewert auf “1”.

Der Aufruf der range-Funktion in Zeile 13 bedarf einer Erläuterung: So wie der Slice "Hallo"[3:2] (erster Index größer oder gleich dem zweiten) einen leeren String liefert, ergibt range eine leere Liste, falls das erste Argument größer oder gleich dem zweiten ist. In diesem Fall springt der Interpreter gleich zu Zeile 15.

Nach der Funktion zum Berechnen der Fakultät steht in den Zeilen 17 bis 26 eine Testfunktion. Üblicherweise bedeutet ein führender Unterstrich in einem Bezeichner, das dieser nur intern (hier in dem Modul) zum Einsatz kommt.

In den Zeilen 28 bis 32 steht etwas Testcode. Dieser ruft die vorher definierte Testfunktion auf – aber nur, wenn Sie das Modul auf der Kommandozeile als Script ausführen. Sonst ignoriert der Interpreter der Code nach Zeile 28. In der if-Bedingung ist __name__ der Name des Moduls, der bei Ausführung von der Kommandozeile der String '__main__' ist, sonst der Name des Moduls. Dieses Idiom finden Sie in Python-Modulen sehr häufig.

Das Modul fakultaet verwenden Sie in eigenem Code so:

import fakultaet
# gibt 720 aus
print fakultaet.fakultaet(6)

Mit der import-Anweisung laden Sie das Modul. Danach stehen über den Modulnamen alle Bezeichner im importierten Modul zur Verfügung; in fakultaet.fakultaet(6) steht der erste Bezeichner für das Modul, der zweite für die Funktion darin. Der Punkt dazwischen heißt Attributzugriffs-Operator, weil Sie damit auf das rechts davon stehende Attribut (hier die Funktion) des links davon stehenden Objekts (hier das Modul) zugreifen.

Das links vom Punkt stehende Objekt heißt auch Namensraum, weil Sie von dort aus auf die darin definierten Namen zugreifen. Wahrscheinlich haben Sie sich gefragt, ob Sie nicht die Funktion verwenden können, ohne das Modul davor zu schreiben. Das geht mit der Form from modul import name, also im Beispiel

from fakultaet import fakultaet
print fakultaet(6)

Benötigen Sie nur wenige Namen, ist der gesparte Schreibaufwand unwichtig; benötigt man viele Namen aus Modulen, ist die ursprünglich vorgestellte Variante beim Lesen des Codes verständlicher. Kurzum: Verwenden Sie lieber nur die Form import Modul (oder from Paket import Modul, siehe unten).

Die Standard-Distribution

Ein Motto im Zusammenhang mit Python lautet “Batteries included” und meint die Auswahl hunderter nützlicher Module, die mit der Standard-Distribution mitkommen. Tabelle 2 listet einige gebräuchliche Module auf. Unter [5] finden Sie alle Module der Distribution auf einen Blick; die Python Library Reference [6] gliedert die Module nach Themen.

Einige Module der Standard-Distribution

Modul Beschreibung
cgi CGI-Support (es gibt außerdem zahlreiche Web-Frameworks [7])
cgitb ausführliche Fehlermeldungen für die CGI-Entwicklung
ConfigParser Parser für Konfigurationsdateien
csv Unterstützung kommaseparierter (CSV) Dateien
datetime Datentypen und Arithmetik für Datum und Uhrzeit
distutils Werkzeuge zum Erstellen und Installieren von Python-Paketen
doctest automatisierte Tests in Docstrings, tolles Konzept!
email Parsen und Erstellen von E-Mails, auch mit Anhängen
ftplib FTP-Client-Bibliothek (einfacher geht’s mit ftputil [8])
gettext Schnittstelle zu GNU gettext
glob plattformübergreifende Wildcard-Unterstützung
gzip Lesen und Schreiben von gzip-Dateien
hotshot Profiler (Geschwindigkeitsanalysator) für Python-Code
imaplib IMAP-Client-Bibliothek
inspect zahlreiche Informationen über Python-Objekte während der Programmlaufzeit ermitteln
logging Logdateien schreiben
mailbox Parsen von Mailbox-Dateien
math mathematische Funktionen
nntplib NNTP (Usenet)-Client-Bibliothek
optparse vielseitige Unterstützung für Kommandozeilen-Schnittstellen
os verschiedene Betriebssystem-Schnittstellen (Dateien und Verzeichnisse und Prozesse)
os.path Bearbeiten von Pfadnamen, Dateieigenschaften ermitteln; wird durch import os automatisch mit importiert
pdb Python-Debugger
pickle Python-Objekte in Strings wandeln und umgekehrt
poplib POP3-Client-Bibliothek
pprint übersichtliche Darstellung von Datenstrukturen
pydoc Dokumentation zu Python-Objekten extrahieren
re reguläre Ausdrücke
shutil Dateioperationen (kopieren, verschieben usw.)
smtplib SMTP-Client-Bibliothek
StringIO Datei-Schnittstelle für Strings
sys Schnittstellen zum Python-Interpreter
tarfile tar-Dateien lesen und schreiben
threading Multithreading-Unterstützung
time Datums-/Zeit-Operationen (vgl. datetime)
urllib, urllib2 verallgemeinerter Zugriff auf URLs
xml.dom DOM-API für XML
xml.sax SAX-API für XML
zipfile Lesen und Schreiben von Zip-Dateien
zlib Komprimieren und Entpacken mit dem zlib-Algorithmus (vgl. gzip)

Modul, wo bist du?

Python findet zu importierende Module mit Hilfe der Verzeichnisliste sys.path. Es ist möglich, diese während der Laufzeit eines Programms verändern. Bei einem import Modulname sucht der Interpreter nacheinander alle Verzeichnisse nach dem Modul Modulname ab.

Die zugehörige Datei ist meistens eine Python-Datei (Modulname.py), seltener eine dynamisch zu bindende Bibliothek (Modulname.so). Da das Bibliotheksverzeichnis der Python-Distribution stets im Suchpfad ist, brauchen Sie für mitgelieferte Module diesen Pfad nicht extra zu setzen.

Paketdienst

Besteht Ihre Python-Software aus zahlreichen Modulen, bietet es sich an, diese in Unterverzeichnissen zu verstauen. Diese Unterverzeichnisse entsprechen sogenannten Paketen, wenn sie eine Datei __init__.py enthalten (die leer sein darf). Entspricht die Verzeichnisstruktur beispielweise Listing 3 importieren Sie mit

from tools import template
from web import request

einzelne Module aus den Paketen. Damit landen die Namen template und request im Namensraum desjenigen Moduls, das diese import-Anweisungen enthält.

Listing 3

tools/
    __init__.py
    database.py
    template.py
web/
    __init__.py
    form.py
    request.py
    response.py

Der Autor

Dr.-Ing. Stefan Schwarzer verwendet Python seit sechs Jahren und hat bei Addison-Wesley “Workshop Python” veröffentlicht. Er ist selbstständiger Softwareentwickler und ist unter mailto:sschwarzer@sschwarzer.net zu erreichen.

LinuxUser 10/2006 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben