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=](https://www.linux-community.de/wp-content/uploads/2010/09/Abbildung-1-184-300x231.jpg)
pprint. Die Python-Distribution enthält Pydoc. Hier bildet es die Grundlage für den help-Befehl im interaktiven Interpreter.” width=”300″ height=”231″ />
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.
Infos
[1] Pydoc: http://docs.python.org/lib/module-pydoc.html, http://pydoc.org/
[2] Generatorfunktionen: http://www.python.org/doc/2.2.3/whatsnew/node5.html
[3] Eingebaute Funktionen: http://docs.python.org/lib/built-in-funcs.html
[4] Diskussions-Thread zu import: http://python.net/pipermail/python-de/2006q2/007705.html
[5] Liste der Python-Module: http://docs.python.org/lib/modindex.html
[6] Python Library Reference: http://docs.python.org/lib/contents.html
[7] Web-Frameworks: http://wiki.python.org/moin/WebProgramming
[8] FTP-Client-Bibliothek ftputil: http://ftputil.sschwarzer.net/




