43
freiesMagazin Python-Sonderausgabe April 2011 Diese Sonderausgabe von freiesMagazin beschäftigt sich allein mit dem Thema Python- Programmierung. Auf den Wunsch der Leser hin wurden die sechs Artikel von Daniel Nögel, die in den freiesMagazin-Ausgaben Oktober 2010 bis März 2011 erschienen sind, zusammengefasst und liegen nun als zusammenhängende Kompilation vor. Auf die Art kann jeder Python-interessierte Neuling die erste Schritte in der Python-Welt wagen und lernt so neben den Grundstrukturen und Sprachelementen der Sprache in den fortge- schrittenen Teilen auch die Anbindung an einer Datenbank, um eine Musikverwaltung aufzubauen. Wir wünschen viel Spaß mit dieser Python-Sonderausgabe. Ihre freiesMagazin-Redaktion © freiesMagazin CC-BY-SA 3.0 Python-Sonderausgabe 04-02/2011 ISSN 1867-7991

freiesfreiesmagazin.de/ftp/2011/freiesMagazin-2011-04-02.pdf · PROGRAMMIERUNG Python-Programmierung: Teil 1 – Hallo Welt von Daniel Nögel P ython erfreut sich seit einiger Zeit

Embed Size (px)

Citation preview

freiesMagazin Python-Sonderausgabe April 2011

Diese Sonderausgabe von freiesMagazin beschaumlftigt sich allein mit dem Thema Python-Programmierung Auf den Wunsch der Leser hin wurden die sechs Artikel von DanielNoumlgel die in den freiesMagazin-Ausgaben Oktober 2010 bis Maumlrz 2011 erschienen sindzusammengefasst und liegen nun als zusammenhaumlngende Kompilation vor Auf die Artkann jeder Python-interessierte Neuling die erste Schritte in der Python-Welt wagen undlernt so neben den Grundstrukturen und Sprachelementen der Sprache in den fortge-schrittenen Teilen auch die Anbindung an einer Datenbank um eine Musikverwaltungaufzubauen

Wir wuumlnschen viel Spaszlig mit dieser Python-Sonderausgabe

Ihre freiesMagazin-Redaktion

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 ISSN 1867-7991

PROGRAMMIERUNG

Python-Programmierung Teil 1 ndash Hallo Welt von Daniel Noumlgel

P ython erfreut sich seit einiger Zeitgroszliger Beliebtheit bei Anfaumlngern undFortgeschrittenen Die Sprache uumlber-

zeugt durch Einfachheit und die groszlige Zahlmitgelieferter Bibliotheken Sowohl als Skript-Sprache etwa fuumlr GIMP als auch bei eigen-staumlndigen Projekten ist Python immer haumlu-figer anzutreffen Bekannte Programme wiePiTiVi oder BitTorrent setzen auf Python undauch im Hintergrund von Google und Youtubesoll die maumlchtige Sprache nicht mehr wegzu-denken sein Dieser Artikel bildet den Anfangeiner mehrteiligen Einfuumlhrung in die Program-miersprache Python

Python ist mittlerweile bei allen groszligen

Die interaktive Python-Konsole nach dem Start

Distributionen vorinstalliert ndash meist inder Version 26+ In der Version 30gab es einige groumlszligere Aumlnderungendie hier ndash wo moumlglich ndash bereits uumlber-nommen werden Wo nicht moumlglichwird auf die bevorstehende Aumlnderunghingewiesen

Gleichzeitig sei aber auch angemerktdass diese Einfuumlhrung nicht jede Me-thode und jede Funktion eroumlrtern kanndie zum Standardrepertoire von Python gehoumlrtVielmehr soll ein Einblick in die Sprache geliefertwerden der ndash wo es notwendig ist ndash hilfreicheund wichtige Methoden kurz anspricht Fuumlr tiefergehende Einblicke empfiehlt sich beispielsweisedie offizielle Dokumentation von Python [1]

Die interaktive ShellPython-Skripte werden nicht kompiliert sondernzur Laufzeit von einem Interpreter ausgefuumlhrtPython-Skripte sind somit immer direkt lauffaumlhigDer Python-Interpreter hat zudem einen interak-tiven Modus ndash hier koumlnnen Befehle direkt abge-setzt werden

Dieser interaktive Modus kann in einem Terminalmit dem Befehl

$ python

gestartet werden

Hinter der Eingabeaufforderung (gtgtgt) koumln-nen beliebige Python-Befehle abgesetzt werden

Die Befehlszeile

gtgtgt print(Hallo Python)

gibt in der naumlchsten Zeile erwartungsgemaumlszligHallo Python aus Auch Berechnungen las-sen sich direkt in ihr durchfuumlhren

gtgtgt 3+710gtgtgt 71070gtgtgt 3-7-4gtgtgt 842gtgtgt832

Die wichtigsten mathematischen Operatorensind damit gleich bekannt Die Verwendung vonbdquo+ldquo bdquo-ldquo bdquoldquo und bdquoldquo sollte keine Schwierigkeitenbereiten Auffaumlllig erscheint allerdings das letzteErgebnis In den Python-Versionen vor 30 gehtder Python-Interpreter bei Divisionen davon ausdass der Benutzer eine Ganzzahldivision durch-fuumlhren moumlchte Erst die Eingabe

gtgtgt 830

fuumlhrt zum erwuumlnschten Ergebnis und zeigt auchNachkommastellen an [2]

Die interaktive Konsole ist ideal um erste Erfah-rungen mit Python zu sammeln Fuumlr groumlszligere Pro-jekte empfiehlt sich aber ein Texteditor

Im Folgenden soll es so gehalten werden dassCodebloumlcke mit Eingabeaufforderung (gtgtgt) im-mer in der Python-Konsole ausgefuumlhrt werdenZeilen ohne diese Zeichen sind als Ausgabe derKonsole zu interpretieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 2

PROGRAMMIERUNG

Codebloumlcke ohne Eingabeaufforderung sindmeist als allgemeine Beispiele und Veranschau-lichungen zu verstehen

Hallo WeltEin erstes kleines Skript ist im Texteditor derWahl schnell erstellt Anders als in der interakti-ven Python-Konsole wo Eingaben direkt berech-net und auf den Bildschirm ausgegeben werdenbenoumltigt man in eigenstaumlndigen Skripten eineFunktion welche die gewuumlnschten Informationenauf dem Bildschirm ausgibt ndash dazu dient in Py-thon die print()-Funktion Nur in der interakti-ven Konsole kann auf diese Funktion in der Re-gel verzichtet werden (wie etwa bei den mathe-matischen Operationen oben)

usrbinenv python -- coding utf-8 --

print(Hallo Welt)

Listing 1 hello_worldpy

Diese Zeilen werden als hello_worldpy ge-speichert Die Datei hello_worldpy wird nunmit dem Befehl

$ chmod +x hello_worldpy

als ausfuumlhrbar markiert und schlieszliglich mit

$ hello_worldpy

oder

$ python hello_worldpy

gestartet Es erscheint folgende Meldung im Ter-minal

Hallo Welt

Das erste kleine Python-Skript ist funktionsfaumlhigBei der ersten Zeile handelt es sich um die soge-nannte Shebang-Zeile (siehe bdquoShebang ndash All derKramldquo freiesMagazin 112009 [3]) Hier wird fest-gelegt dass die Datei mit dem Python-Interpreterauszufuumlhren ist Die zweite Zeile informiert denPython-Interpreter uumlber die verwendete Zeichen-kodierung Diese beiden Zeilen sollten in allenPython-Dateien vorhanden sein Ohne Hinweisauf die Kodierung kann es zu Problemen mit Um-lauten in Zeichenketten kommen

Das erste bdquonuumltzlicheldquo ProgrammAls naumlchstes soll ein etwas ambitionierteres Pro-jekt in Angriff genommen werden Der Benutzersoll seinen Namen eingeben koumlnnen und diesendann in einer Box aus Gleichheitszeichen darge-stellt bekommen

usrbinenv python -- coding utf-8 --

name = raw_input(Hallo Wie heisst du )name_with_borders = = 0 =format(name)line = = len(name_with_borders)

print(line)print(name_with_borders)print(line)

Listing 2 your_namepy

Hier gibt es schon Neues zu sehen In der viertenZeile wird der Benutzer nach seinem Namen ge-fragt Die Funktion raw_input() gibt die als Pa-rameter uumlbergebene Zeichenkette auf dem Bild-schirm aus und wartet dann auf die Eingabe desBenutzers Diese wird nach dem Druumlcken vonEnter in der Variable name gespeichert (genau-er es wird ein Zeichenketten-Objekt erstellt aufdas die Variable verweist) Erst danach werdendie uumlbrigen Zeilen des Skriptes verarbeitet

Achtung Ab Python 3 heiszligt es input() undnicht mehr raw_input() Bis Python 3 ist vonder Verwendung von input() dringend abzura-ten weil die Benutzereingabe direkt vom Inter-preter ausgefuumlhrt wird ndash das ist in den allermeis-ten Faumlllen nicht erwuumlnscht

In Zeile 5 wird die Eingabe des Nutzers mitGleichheitszeichen umgeben Dazu wird die Zei-chenkette = 0 = erzeugt und die Zeichenfol-ge 0 mit Hilfe der format()-Methode durchden Inhalt der Variable name ersetzt In einem

spaumlteren Teil dieser Reihe wird aufdiese Art der Ersetzung genauer ein-gegangen werden In Zeile 6 wirdmit der Funktion len() die Laumln-ge des Benutzernamens inklusiveGleichheits- und Leerzeichen ermit-telt und dann die entsprechende An-zahl von Gleichheitszeichen ausge-geben Dazu wird hier der -Operatorbenutzt ndash auch Zeichenketten lassensich in Python also bdquomultiplizierenldquoSo entsteht eine Folge von Gleich-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 3

PROGRAMMIERUNG

heitszeichen die so lang ist wie die Zeichenkettename_with_borders

In Zeile 9 wird die in Zeile 5 erstellte Zeichenket-te auf dem Bildschirm ausgegeben Zeilen 8 und10 geben jeweils die in Zeile 6 erstellte bdquoLinieldquo aufdem Bildschirm aus Das Gesamtergebnis siehtdann wie folgt aus

Hallo Wie heisst du Margot=========== Margot ===========

ZeichenkettenJetzt wurden bereits erste Erfahrungen mit Zei-chenketten gesammelt Diese sollen hier vertieftwerden Zeichenketten muumlssen immer von An-fuumlhrungszeichen umschlossen werden Moumlglichsind einfache und doppelte Anfuumlhrungszeichen

name = Berndname = Bernd

Es gibt keinen Unterschied zwischen diesen bei-den Varianten ndash die Verwendung ist letztlichGeschmackssache Allerdings sollte darauf ge-achtet werden dass innerhalb einer Zeichenket-te keine aumluszligeren Anfuumlhrungsstriche vorkommenkoumlnnen

message = Ich heisse Bernd

fuumlhrt also zu einem Fehler Stattdessen kann insolchen Faumlllen der jeweils andere Anfuumlhrungs-strich genutzt werden

message = Ich heisse Bernd

oder

message = Ich heisse Bernd

In Python gibt es auszligerdem aber noch dreifacheAnfuumlhrungsstriche ( oder rsquorsquorsquo) Innerhalb vondreifachen Anfuumlhrungsstrichen kann man die ein-fachen und doppelten Anfuumlhrungszeichen nachBelieben verwenden und sogar Zeilenumbruumlchesind moumlglich

print(Hier kann ich und nach Belieben einsetzenAusserdem sind sogar Zeilenumbrueche moeglich)

Wie in vielen anderen Sprachen gibt es in Py-thon natuumlrlich auch die Moumlglichkeit Zeichenket-ten durch Voranstellen eines zu bdquoescapenldquo d hso zu markieren dass der Interpreter nicht dar-uumlber stolpert Das fehlerhafte Beispiel von obensaumlhe mit Escape-Zeichen dann so aus

message = Ich heisse Bernd

Mit dem Escape-Zeichen lassen sich auch Zei-lenumbruumlche in Zeichenketten mit einfachen unddoppelten Anfuumlhrungsstrichen erzwingen ndash dieswaumlre sonst nicht moumlglich

gtgtgt print(Ein nZeilenumbruch)EinZeilenumbruch

In der Python-Dokumentation finden sich weitereEscape-Sequenzen [4]

Zeichenketten naumlher betrachtetZeichenketten in Python gehoumlren ndash wie auch Zah-len ndash zu den unveraumlnderbaren Datentypen JedeVeraumlnderung an einer Zeichenkette liefert immereine neue Zeichenkette zuruumlck

gtgtgt text1 = HALLOgtgtgt text2 = text1lower()gtgtgt print(text1)HALLOgtgtgt print(text2)hallo

Hier wurden die Groszlig-buchstaben mit derMethode lower() in

Kleinbuchstaben bdquoumgewandeltldquo Tatsaumlchlichbleibt text1 davon aber unberuumlhrt stattdessenwird eine voumlllig neue Zeichenkette erzeugt Na-tuumlrlich ist es aber moumlglich eine Variable direktneu zuzuweisen so dass die Unveraumlnderbarkeitvon Strings in der Praxis kaum Bedeutung hat

gtgtgt text = HALLOgtgtgt text = textlower()gtgtgt print(text)hallo

Hier wird zunaumlchst der Variable text die Zeichen-kette HALLO zugewiesen Durch die Methodelower() wird eine neue Zeichenkette mit Klein-buchstaben erstellt Nun zeigt die Variable textauf die neu erstellte Zeichenkette Da jetzt kei-ne Variable mehr auf die alte Zeichenkette zeigtwird diese bei Zeiten automatisch aus dem Spei-cher geloumlscht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 4

PROGRAMMIERUNG

Analog zu lower() gibt es mit upper() eineMethode die eine Zeichenkette mit ausschlieszlig-lich Groszligbuchstaben erzeugt Mit swapcase()werden kleine Buchstaben zu Groszligbuchstabenund umgekehrt Geradezu unerlaumlsslich ist diereplace()-Methode Sie ersetzt alle Vorkom-men einer gesuchten Zeichenfolge innerhalb ei-ner Zeichenkette

gtgtgt Ich finde Python doofreplacey(doof super)Ich finde Python super

In der Python-Dokumentation finden sich vieleweitere nuumltzliche Zeichenketten-Funktionen [5]

Richtig einruumlckenViele Programmiersprachen kennen bestimmteKontrollstrukturen die den Programmfluss in be-sonderer Weise beeinflussen Hier ein Beispiel inPseudocode

zaehler = 1solange zahler lt= 5 wiederholegib zaehler auf dem Bildschirm auserhoehe zaehler um 1gib fertig auf dem Bildschirm aus

Klar Hier wird der Zaumlhler von 1 bis 5 hoch-gezaumlhlt und jeweils auf dem Bildschirm ausge-geben Aber wie oft wird bdquofertigldquo auf den Bild-schirm geschrieben Es wird deutlich dass demInterpreterCompiler irgendwie mitgeteilt werdenmuss welche Information noch zur Kontrollstruk-tur gehoumlrt und wo der normale Programmflussfortgesetzt wird

Viele andere Programmiersprachen loumlsen dasProblem mit geschweiften Klammern die Anfangund Ende des auszufuumlhrenden Codeblocks mar-kieren In Python gibt es derartige Klammernnicht ndash zusammengehoumlrende Codebloumlcke muumls-sen gemeinsam eingeruumlckt werden

zaehler = 1solange zahler lt= 5 wiederhole

gib zaehler auf dem Bildschirm yauserhoehe zaehler um 1

gib fertig auf dem Bildschirm aus

So weiszlig der Python-Interpreter dass nur Zeilen3 und 4 zur Kontrollstruktur gehoumlren bdquofertigldquo wirdnur einmal auf dem Bildschirm ausgegeben Wauml-re auch Zeile 5 eingeruumlckt wuumlrde bdquofertigldquo eben-falls fuumlnfmal ausgegeben

Das Einruumlcken ist eine Besonderheit von Py-thon und einigen wenigen anderen Sprachen diedas Lesen des Quelltextes vereinfachen soll DerBenutzer wird gezwungen sinnvoll einzuruumlckenWie viel eingeruumlckt wird und ob es mit Leerzei-chen oder Tabulatoren geschieht bleibt dem Be-nutzer uumlberlassen ndash es muss nur einheitlich seinSobald Leerzeichen und Tabulatoren gemischtwerden oder an einer Stelle mit vier Leerzeicheneingeruumlckt wird an anderer aber mit drei kommtes zu Problemen und das Programm wird sehrwahrscheinlich nicht richtig ausgefuumlhrt werdenAllgemein wird das Einruumlcken mit vier Leerzei-chen empfohlen Fast jeder gaumlngige Texteditorist heute in der Lage statt Tabulatoren Leerzei-

chen einzufuumlgen so dass dem Benutzer durchdie Verwendung von Leerzeichen keinerlei Nach-teile entstehen

for-SchleifeZuletzt soll hier nun die for-Schleife besprochenwerden Mit dieser Schleife kann man beliebigeAnweisungen beliebig oft ausfuumlhren lassen Mitfolgendem einfachen Beispiel werden zehn Zah-len nacheinander ausgegeben

usrbinenv python -- coding utf-8 --

for i in range(0 10)print(i)

print(Fertig)

Listing 3 for_schleifepy

Die Ausgabe des obigen Skriptes sieht wie folgtaus

0123456789Fertig

Was passiert in dem obigen Beispiel genauDie Funktion range() liefert (vereinfacht gesagt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 5

PROGRAMMIERUNG

es gibt hier Unterschiede in den verschiedenenPython-Versionen) eine Liste von 0 bis 9 zuruumlckDie Anweisung for i in range(0 10) laumlsstsich umgangssprachlich uumlbersetzen mit bdquoFuumlr je-des Element i in der Liste der Zahlen von 0 bisausschlieszliglich 10 mache ldquo

Die Liste der Zahlen von 0 bis 9 wird also schritt-weise durchlaufen Zunaumlchst wird i der Wert 0zugewiesen und dann die Anweisung print(i)ausgefuumlhrt Danach wird i der Wert 1 zugewie-sen und erneut der Anweisungsblock ausgefuumlhrtetc Man nennt dieses Vorgehen auch bdquoIterationldquoHier wird also uumlber die von range() erzeugte Lis-te bdquoiteriertldquo

An diesem Beispiel wird auch deutlich warumrichtiges Einruumlcken so wichtig ist Haumltte man die

Zeile print(Fertig) in Zeile 6 auch einge-ruumlckt waumlre diese Anweisung bei jedem Schlei-fendurchlauf ausgefuumlhrt worden ndash und nicht erstnach dem Durchlaufen der Schleife

Mit der for-Schleife endet der erste Teil der Ein-fuumlhrung in Python Im naumlchsten Teil werden dannif- und while-Bloumlcke sowie Listen besprochen

LINKS

[1] httpdocspythonorg[2] httpwwwpythonorgdevpepspep-0238[3] httpwwwfreiesmagazindefreiesMagazin-2009-

11[4] httpwwwpythonorgdoccurrentreference

lexical_analysishtmligrammar-token-escapeseq

[5] httpdocspythonorglibrarystdtypeshtmlstring-methods

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoBoyfriendldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom539

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 6

PROGRAMMIERUNG

Python-Programmierung Teil 2 ndash Tiefere Einblicke von Daniel Noumlgel

Im vorherigen Teil der Python-ReihebdquoPython-Programmierung Teil 1 ndash HalloWeltldquo auf Seite 2 wurden erste Erfahrun-

gen mit mathematischen Operatoren Zei-chenketten und for-Schleifen gesammelt Imzweiten Teil sollen nun besonders Listen inPython betrachtet werden Mit if und whilewerden aber auch zwei weitere Kontrollstruk-turen vorgestellt

Korrekturen und ErgaumlnzungenIm ersten Teil wurde bereits angesprochen dassmanche Spracheigenschaften von Python sichab Version 3x geaumlndert haben Dazu gehoumlreninsbesondere die Zeichenketten Erst ab 3x ar-beitet Python immer mit Unicode-ZeichenkettenDavor muss die Verwendung von Unicode bei derErstellung von Zeichenketten erzwungen werdenUnterbleibt dies koumlnnen schnell schwer zu ermit-telnde Probleme auftreten Auch Zeichenkettenaus Dateien und anderen Quellen sollten so fruumlhwie moumlglich in Unicode umgewandelt werden AbPython 3 muss sich der Entwickler darum nichtmehr kuumlmmern

Eine Unicode-Zeichenkette wird in Python 2xdurch das Voranstellen eines u vor die Zeichen-kette oder den Aufruf der Funktion unicode() er-stellt [1] [2] [3]

uIch bin ein Unicode-Stringunicode(Auch ich werde eine yUnicode-Zeichenkette)

Bei Python-Versionen lt 3 bin ich yein normaler Byte-String

Ein weiterer Unterschied zu Python 3x der imletzten Teil verschwiegen wurde ist die Verwen-dung von print Erst ab Python 3 wird print alsFunktion verwendet und muss wie folgt aufgeru-fen werden

gtgtgt print(Hallo Welt)

Vor Python 3 wurde print als Statement imple-mentiert

gtgtgt print Hallo Welt

Hinweis Wie schon im ersten Teil verein-bart werden Zeilen die mit gtgtgt beginnendirekt in die interaktive Konsole von Python

Eine Auswahl von Operatoren in PythonOperator Typ Funktion+ - Mathematisch Addition Subtraktion Multiplikation

Division Mathematisch Potenzierunglt gt lt= gt= Vergleich kleiner als groumlszliger als kleiner als

oder gleich groumlszliger als oder gleich== Vergleich gleich= Vergleich ungleich= Zuweisung weist einen Wert zuin Listen-Operator

Mitgliedschaftstesttestet ob der rechte Operand Mit-glied im linken Operanden ist

and Bool Operator Konjunktion logisches Undor Bool Operator Disjunktion logisches Odernot Bool Operator Negation logische Verneinung

eingegeben ndash dann natuumlrlich ohnedie spitzen Klammern

Die genauen Unterschiede sollenhier weiter keine Rolle spielenWichtig ist nur Nutzer von Python3x verwenden print als Funktion(mit Klammern) Nutzer von Py-thon 2x verwenden print ohneKlammern

Da heute noch zumeist Python 2xverwendet wird und viele Bibliothe-ken fuumlr Python 3x noch nicht an-gepasst wurden werden ab die-sem zweiten Teil die hier genann-

ten Ergaumlnzungen beruumlcksichtigt Allen Zeichen-ketten wird von nun an also ein u vorangestelltum Unicode-Zeichenketten zu erzeugen Benut-zereingaben werden im Folgenden mit der Funk-tion unicode() ebenfalls in Unicode umgewan-delt Nutzer von Python 3x muumlssen das voran-gestellte u und die Funktion unicode() jeweilsauslassen ndash in 3x wird ja ohnehin immer mit Uni-code gearbeitet

OperatorenBevor nun in den naumlchsten Abschnitten if- undwhile-Bloumlcke behandelt werden sollen zuerst ei-nige Operatoren besprochen werden Operato-ren sind ndash vereinfacht gesagt ndash (mathematische)Vorschriften durch die aus zwei Objekten einneues Objekt gebildet wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 7

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Python-Programmierung Teil 1 ndash Hallo Welt von Daniel Noumlgel

P ython erfreut sich seit einiger Zeitgroszliger Beliebtheit bei Anfaumlngern undFortgeschrittenen Die Sprache uumlber-

zeugt durch Einfachheit und die groszlige Zahlmitgelieferter Bibliotheken Sowohl als Skript-Sprache etwa fuumlr GIMP als auch bei eigen-staumlndigen Projekten ist Python immer haumlu-figer anzutreffen Bekannte Programme wiePiTiVi oder BitTorrent setzen auf Python undauch im Hintergrund von Google und Youtubesoll die maumlchtige Sprache nicht mehr wegzu-denken sein Dieser Artikel bildet den Anfangeiner mehrteiligen Einfuumlhrung in die Program-miersprache Python

Python ist mittlerweile bei allen groszligen

Die interaktive Python-Konsole nach dem Start

Distributionen vorinstalliert ndash meist inder Version 26+ In der Version 30gab es einige groumlszligere Aumlnderungendie hier ndash wo moumlglich ndash bereits uumlber-nommen werden Wo nicht moumlglichwird auf die bevorstehende Aumlnderunghingewiesen

Gleichzeitig sei aber auch angemerktdass diese Einfuumlhrung nicht jede Me-thode und jede Funktion eroumlrtern kanndie zum Standardrepertoire von Python gehoumlrtVielmehr soll ein Einblick in die Sprache geliefertwerden der ndash wo es notwendig ist ndash hilfreicheund wichtige Methoden kurz anspricht Fuumlr tiefergehende Einblicke empfiehlt sich beispielsweisedie offizielle Dokumentation von Python [1]

Die interaktive ShellPython-Skripte werden nicht kompiliert sondernzur Laufzeit von einem Interpreter ausgefuumlhrtPython-Skripte sind somit immer direkt lauffaumlhigDer Python-Interpreter hat zudem einen interak-tiven Modus ndash hier koumlnnen Befehle direkt abge-setzt werden

Dieser interaktive Modus kann in einem Terminalmit dem Befehl

$ python

gestartet werden

Hinter der Eingabeaufforderung (gtgtgt) koumln-nen beliebige Python-Befehle abgesetzt werden

Die Befehlszeile

gtgtgt print(Hallo Python)

gibt in der naumlchsten Zeile erwartungsgemaumlszligHallo Python aus Auch Berechnungen las-sen sich direkt in ihr durchfuumlhren

gtgtgt 3+710gtgtgt 71070gtgtgt 3-7-4gtgtgt 842gtgtgt832

Die wichtigsten mathematischen Operatorensind damit gleich bekannt Die Verwendung vonbdquo+ldquo bdquo-ldquo bdquoldquo und bdquoldquo sollte keine Schwierigkeitenbereiten Auffaumlllig erscheint allerdings das letzteErgebnis In den Python-Versionen vor 30 gehtder Python-Interpreter bei Divisionen davon ausdass der Benutzer eine Ganzzahldivision durch-fuumlhren moumlchte Erst die Eingabe

gtgtgt 830

fuumlhrt zum erwuumlnschten Ergebnis und zeigt auchNachkommastellen an [2]

Die interaktive Konsole ist ideal um erste Erfah-rungen mit Python zu sammeln Fuumlr groumlszligere Pro-jekte empfiehlt sich aber ein Texteditor

Im Folgenden soll es so gehalten werden dassCodebloumlcke mit Eingabeaufforderung (gtgtgt) im-mer in der Python-Konsole ausgefuumlhrt werdenZeilen ohne diese Zeichen sind als Ausgabe derKonsole zu interpretieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 2

PROGRAMMIERUNG

Codebloumlcke ohne Eingabeaufforderung sindmeist als allgemeine Beispiele und Veranschau-lichungen zu verstehen

Hallo WeltEin erstes kleines Skript ist im Texteditor derWahl schnell erstellt Anders als in der interakti-ven Python-Konsole wo Eingaben direkt berech-net und auf den Bildschirm ausgegeben werdenbenoumltigt man in eigenstaumlndigen Skripten eineFunktion welche die gewuumlnschten Informationenauf dem Bildschirm ausgibt ndash dazu dient in Py-thon die print()-Funktion Nur in der interakti-ven Konsole kann auf diese Funktion in der Re-gel verzichtet werden (wie etwa bei den mathe-matischen Operationen oben)

usrbinenv python -- coding utf-8 --

print(Hallo Welt)

Listing 1 hello_worldpy

Diese Zeilen werden als hello_worldpy ge-speichert Die Datei hello_worldpy wird nunmit dem Befehl

$ chmod +x hello_worldpy

als ausfuumlhrbar markiert und schlieszliglich mit

$ hello_worldpy

oder

$ python hello_worldpy

gestartet Es erscheint folgende Meldung im Ter-minal

Hallo Welt

Das erste kleine Python-Skript ist funktionsfaumlhigBei der ersten Zeile handelt es sich um die soge-nannte Shebang-Zeile (siehe bdquoShebang ndash All derKramldquo freiesMagazin 112009 [3]) Hier wird fest-gelegt dass die Datei mit dem Python-Interpreterauszufuumlhren ist Die zweite Zeile informiert denPython-Interpreter uumlber die verwendete Zeichen-kodierung Diese beiden Zeilen sollten in allenPython-Dateien vorhanden sein Ohne Hinweisauf die Kodierung kann es zu Problemen mit Um-lauten in Zeichenketten kommen

Das erste bdquonuumltzlicheldquo ProgrammAls naumlchstes soll ein etwas ambitionierteres Pro-jekt in Angriff genommen werden Der Benutzersoll seinen Namen eingeben koumlnnen und diesendann in einer Box aus Gleichheitszeichen darge-stellt bekommen

usrbinenv python -- coding utf-8 --

name = raw_input(Hallo Wie heisst du )name_with_borders = = 0 =format(name)line = = len(name_with_borders)

print(line)print(name_with_borders)print(line)

Listing 2 your_namepy

Hier gibt es schon Neues zu sehen In der viertenZeile wird der Benutzer nach seinem Namen ge-fragt Die Funktion raw_input() gibt die als Pa-rameter uumlbergebene Zeichenkette auf dem Bild-schirm aus und wartet dann auf die Eingabe desBenutzers Diese wird nach dem Druumlcken vonEnter in der Variable name gespeichert (genau-er es wird ein Zeichenketten-Objekt erstellt aufdas die Variable verweist) Erst danach werdendie uumlbrigen Zeilen des Skriptes verarbeitet

Achtung Ab Python 3 heiszligt es input() undnicht mehr raw_input() Bis Python 3 ist vonder Verwendung von input() dringend abzura-ten weil die Benutzereingabe direkt vom Inter-preter ausgefuumlhrt wird ndash das ist in den allermeis-ten Faumlllen nicht erwuumlnscht

In Zeile 5 wird die Eingabe des Nutzers mitGleichheitszeichen umgeben Dazu wird die Zei-chenkette = 0 = erzeugt und die Zeichenfol-ge 0 mit Hilfe der format()-Methode durchden Inhalt der Variable name ersetzt In einem

spaumlteren Teil dieser Reihe wird aufdiese Art der Ersetzung genauer ein-gegangen werden In Zeile 6 wirdmit der Funktion len() die Laumln-ge des Benutzernamens inklusiveGleichheits- und Leerzeichen ermit-telt und dann die entsprechende An-zahl von Gleichheitszeichen ausge-geben Dazu wird hier der -Operatorbenutzt ndash auch Zeichenketten lassensich in Python also bdquomultiplizierenldquoSo entsteht eine Folge von Gleich-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 3

PROGRAMMIERUNG

heitszeichen die so lang ist wie die Zeichenkettename_with_borders

In Zeile 9 wird die in Zeile 5 erstellte Zeichenket-te auf dem Bildschirm ausgegeben Zeilen 8 und10 geben jeweils die in Zeile 6 erstellte bdquoLinieldquo aufdem Bildschirm aus Das Gesamtergebnis siehtdann wie folgt aus

Hallo Wie heisst du Margot=========== Margot ===========

ZeichenkettenJetzt wurden bereits erste Erfahrungen mit Zei-chenketten gesammelt Diese sollen hier vertieftwerden Zeichenketten muumlssen immer von An-fuumlhrungszeichen umschlossen werden Moumlglichsind einfache und doppelte Anfuumlhrungszeichen

name = Berndname = Bernd

Es gibt keinen Unterschied zwischen diesen bei-den Varianten ndash die Verwendung ist letztlichGeschmackssache Allerdings sollte darauf ge-achtet werden dass innerhalb einer Zeichenket-te keine aumluszligeren Anfuumlhrungsstriche vorkommenkoumlnnen

message = Ich heisse Bernd

fuumlhrt also zu einem Fehler Stattdessen kann insolchen Faumlllen der jeweils andere Anfuumlhrungs-strich genutzt werden

message = Ich heisse Bernd

oder

message = Ich heisse Bernd

In Python gibt es auszligerdem aber noch dreifacheAnfuumlhrungsstriche ( oder rsquorsquorsquo) Innerhalb vondreifachen Anfuumlhrungsstrichen kann man die ein-fachen und doppelten Anfuumlhrungszeichen nachBelieben verwenden und sogar Zeilenumbruumlchesind moumlglich

print(Hier kann ich und nach Belieben einsetzenAusserdem sind sogar Zeilenumbrueche moeglich)

Wie in vielen anderen Sprachen gibt es in Py-thon natuumlrlich auch die Moumlglichkeit Zeichenket-ten durch Voranstellen eines zu bdquoescapenldquo d hso zu markieren dass der Interpreter nicht dar-uumlber stolpert Das fehlerhafte Beispiel von obensaumlhe mit Escape-Zeichen dann so aus

message = Ich heisse Bernd

Mit dem Escape-Zeichen lassen sich auch Zei-lenumbruumlche in Zeichenketten mit einfachen unddoppelten Anfuumlhrungsstrichen erzwingen ndash dieswaumlre sonst nicht moumlglich

gtgtgt print(Ein nZeilenumbruch)EinZeilenumbruch

In der Python-Dokumentation finden sich weitereEscape-Sequenzen [4]

Zeichenketten naumlher betrachtetZeichenketten in Python gehoumlren ndash wie auch Zah-len ndash zu den unveraumlnderbaren Datentypen JedeVeraumlnderung an einer Zeichenkette liefert immereine neue Zeichenkette zuruumlck

gtgtgt text1 = HALLOgtgtgt text2 = text1lower()gtgtgt print(text1)HALLOgtgtgt print(text2)hallo

Hier wurden die Groszlig-buchstaben mit derMethode lower() in

Kleinbuchstaben bdquoumgewandeltldquo Tatsaumlchlichbleibt text1 davon aber unberuumlhrt stattdessenwird eine voumlllig neue Zeichenkette erzeugt Na-tuumlrlich ist es aber moumlglich eine Variable direktneu zuzuweisen so dass die Unveraumlnderbarkeitvon Strings in der Praxis kaum Bedeutung hat

gtgtgt text = HALLOgtgtgt text = textlower()gtgtgt print(text)hallo

Hier wird zunaumlchst der Variable text die Zeichen-kette HALLO zugewiesen Durch die Methodelower() wird eine neue Zeichenkette mit Klein-buchstaben erstellt Nun zeigt die Variable textauf die neu erstellte Zeichenkette Da jetzt kei-ne Variable mehr auf die alte Zeichenkette zeigtwird diese bei Zeiten automatisch aus dem Spei-cher geloumlscht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 4

PROGRAMMIERUNG

Analog zu lower() gibt es mit upper() eineMethode die eine Zeichenkette mit ausschlieszlig-lich Groszligbuchstaben erzeugt Mit swapcase()werden kleine Buchstaben zu Groszligbuchstabenund umgekehrt Geradezu unerlaumlsslich ist diereplace()-Methode Sie ersetzt alle Vorkom-men einer gesuchten Zeichenfolge innerhalb ei-ner Zeichenkette

gtgtgt Ich finde Python doofreplacey(doof super)Ich finde Python super

In der Python-Dokumentation finden sich vieleweitere nuumltzliche Zeichenketten-Funktionen [5]

Richtig einruumlckenViele Programmiersprachen kennen bestimmteKontrollstrukturen die den Programmfluss in be-sonderer Weise beeinflussen Hier ein Beispiel inPseudocode

zaehler = 1solange zahler lt= 5 wiederholegib zaehler auf dem Bildschirm auserhoehe zaehler um 1gib fertig auf dem Bildschirm aus

Klar Hier wird der Zaumlhler von 1 bis 5 hoch-gezaumlhlt und jeweils auf dem Bildschirm ausge-geben Aber wie oft wird bdquofertigldquo auf den Bild-schirm geschrieben Es wird deutlich dass demInterpreterCompiler irgendwie mitgeteilt werdenmuss welche Information noch zur Kontrollstruk-tur gehoumlrt und wo der normale Programmflussfortgesetzt wird

Viele andere Programmiersprachen loumlsen dasProblem mit geschweiften Klammern die Anfangund Ende des auszufuumlhrenden Codeblocks mar-kieren In Python gibt es derartige Klammernnicht ndash zusammengehoumlrende Codebloumlcke muumls-sen gemeinsam eingeruumlckt werden

zaehler = 1solange zahler lt= 5 wiederhole

gib zaehler auf dem Bildschirm yauserhoehe zaehler um 1

gib fertig auf dem Bildschirm aus

So weiszlig der Python-Interpreter dass nur Zeilen3 und 4 zur Kontrollstruktur gehoumlren bdquofertigldquo wirdnur einmal auf dem Bildschirm ausgegeben Wauml-re auch Zeile 5 eingeruumlckt wuumlrde bdquofertigldquo eben-falls fuumlnfmal ausgegeben

Das Einruumlcken ist eine Besonderheit von Py-thon und einigen wenigen anderen Sprachen diedas Lesen des Quelltextes vereinfachen soll DerBenutzer wird gezwungen sinnvoll einzuruumlckenWie viel eingeruumlckt wird und ob es mit Leerzei-chen oder Tabulatoren geschieht bleibt dem Be-nutzer uumlberlassen ndash es muss nur einheitlich seinSobald Leerzeichen und Tabulatoren gemischtwerden oder an einer Stelle mit vier Leerzeicheneingeruumlckt wird an anderer aber mit drei kommtes zu Problemen und das Programm wird sehrwahrscheinlich nicht richtig ausgefuumlhrt werdenAllgemein wird das Einruumlcken mit vier Leerzei-chen empfohlen Fast jeder gaumlngige Texteditorist heute in der Lage statt Tabulatoren Leerzei-

chen einzufuumlgen so dass dem Benutzer durchdie Verwendung von Leerzeichen keinerlei Nach-teile entstehen

for-SchleifeZuletzt soll hier nun die for-Schleife besprochenwerden Mit dieser Schleife kann man beliebigeAnweisungen beliebig oft ausfuumlhren lassen Mitfolgendem einfachen Beispiel werden zehn Zah-len nacheinander ausgegeben

usrbinenv python -- coding utf-8 --

for i in range(0 10)print(i)

print(Fertig)

Listing 3 for_schleifepy

Die Ausgabe des obigen Skriptes sieht wie folgtaus

0123456789Fertig

Was passiert in dem obigen Beispiel genauDie Funktion range() liefert (vereinfacht gesagt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 5

PROGRAMMIERUNG

es gibt hier Unterschiede in den verschiedenenPython-Versionen) eine Liste von 0 bis 9 zuruumlckDie Anweisung for i in range(0 10) laumlsstsich umgangssprachlich uumlbersetzen mit bdquoFuumlr je-des Element i in der Liste der Zahlen von 0 bisausschlieszliglich 10 mache ldquo

Die Liste der Zahlen von 0 bis 9 wird also schritt-weise durchlaufen Zunaumlchst wird i der Wert 0zugewiesen und dann die Anweisung print(i)ausgefuumlhrt Danach wird i der Wert 1 zugewie-sen und erneut der Anweisungsblock ausgefuumlhrtetc Man nennt dieses Vorgehen auch bdquoIterationldquoHier wird also uumlber die von range() erzeugte Lis-te bdquoiteriertldquo

An diesem Beispiel wird auch deutlich warumrichtiges Einruumlcken so wichtig ist Haumltte man die

Zeile print(Fertig) in Zeile 6 auch einge-ruumlckt waumlre diese Anweisung bei jedem Schlei-fendurchlauf ausgefuumlhrt worden ndash und nicht erstnach dem Durchlaufen der Schleife

Mit der for-Schleife endet der erste Teil der Ein-fuumlhrung in Python Im naumlchsten Teil werden dannif- und while-Bloumlcke sowie Listen besprochen

LINKS

[1] httpdocspythonorg[2] httpwwwpythonorgdevpepspep-0238[3] httpwwwfreiesmagazindefreiesMagazin-2009-

11[4] httpwwwpythonorgdoccurrentreference

lexical_analysishtmligrammar-token-escapeseq

[5] httpdocspythonorglibrarystdtypeshtmlstring-methods

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoBoyfriendldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom539

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 6

PROGRAMMIERUNG

Python-Programmierung Teil 2 ndash Tiefere Einblicke von Daniel Noumlgel

Im vorherigen Teil der Python-ReihebdquoPython-Programmierung Teil 1 ndash HalloWeltldquo auf Seite 2 wurden erste Erfahrun-

gen mit mathematischen Operatoren Zei-chenketten und for-Schleifen gesammelt Imzweiten Teil sollen nun besonders Listen inPython betrachtet werden Mit if und whilewerden aber auch zwei weitere Kontrollstruk-turen vorgestellt

Korrekturen und ErgaumlnzungenIm ersten Teil wurde bereits angesprochen dassmanche Spracheigenschaften von Python sichab Version 3x geaumlndert haben Dazu gehoumlreninsbesondere die Zeichenketten Erst ab 3x ar-beitet Python immer mit Unicode-ZeichenkettenDavor muss die Verwendung von Unicode bei derErstellung von Zeichenketten erzwungen werdenUnterbleibt dies koumlnnen schnell schwer zu ermit-telnde Probleme auftreten Auch Zeichenkettenaus Dateien und anderen Quellen sollten so fruumlhwie moumlglich in Unicode umgewandelt werden AbPython 3 muss sich der Entwickler darum nichtmehr kuumlmmern

Eine Unicode-Zeichenkette wird in Python 2xdurch das Voranstellen eines u vor die Zeichen-kette oder den Aufruf der Funktion unicode() er-stellt [1] [2] [3]

uIch bin ein Unicode-Stringunicode(Auch ich werde eine yUnicode-Zeichenkette)

Bei Python-Versionen lt 3 bin ich yein normaler Byte-String

Ein weiterer Unterschied zu Python 3x der imletzten Teil verschwiegen wurde ist die Verwen-dung von print Erst ab Python 3 wird print alsFunktion verwendet und muss wie folgt aufgeru-fen werden

gtgtgt print(Hallo Welt)

Vor Python 3 wurde print als Statement imple-mentiert

gtgtgt print Hallo Welt

Hinweis Wie schon im ersten Teil verein-bart werden Zeilen die mit gtgtgt beginnendirekt in die interaktive Konsole von Python

Eine Auswahl von Operatoren in PythonOperator Typ Funktion+ - Mathematisch Addition Subtraktion Multiplikation

Division Mathematisch Potenzierunglt gt lt= gt= Vergleich kleiner als groumlszliger als kleiner als

oder gleich groumlszliger als oder gleich== Vergleich gleich= Vergleich ungleich= Zuweisung weist einen Wert zuin Listen-Operator

Mitgliedschaftstesttestet ob der rechte Operand Mit-glied im linken Operanden ist

and Bool Operator Konjunktion logisches Undor Bool Operator Disjunktion logisches Odernot Bool Operator Negation logische Verneinung

eingegeben ndash dann natuumlrlich ohnedie spitzen Klammern

Die genauen Unterschiede sollenhier weiter keine Rolle spielenWichtig ist nur Nutzer von Python3x verwenden print als Funktion(mit Klammern) Nutzer von Py-thon 2x verwenden print ohneKlammern

Da heute noch zumeist Python 2xverwendet wird und viele Bibliothe-ken fuumlr Python 3x noch nicht an-gepasst wurden werden ab die-sem zweiten Teil die hier genann-

ten Ergaumlnzungen beruumlcksichtigt Allen Zeichen-ketten wird von nun an also ein u vorangestelltum Unicode-Zeichenketten zu erzeugen Benut-zereingaben werden im Folgenden mit der Funk-tion unicode() ebenfalls in Unicode umgewan-delt Nutzer von Python 3x muumlssen das voran-gestellte u und die Funktion unicode() jeweilsauslassen ndash in 3x wird ja ohnehin immer mit Uni-code gearbeitet

OperatorenBevor nun in den naumlchsten Abschnitten if- undwhile-Bloumlcke behandelt werden sollen zuerst ei-nige Operatoren besprochen werden Operato-ren sind ndash vereinfacht gesagt ndash (mathematische)Vorschriften durch die aus zwei Objekten einneues Objekt gebildet wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 7

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Codebloumlcke ohne Eingabeaufforderung sindmeist als allgemeine Beispiele und Veranschau-lichungen zu verstehen

Hallo WeltEin erstes kleines Skript ist im Texteditor derWahl schnell erstellt Anders als in der interakti-ven Python-Konsole wo Eingaben direkt berech-net und auf den Bildschirm ausgegeben werdenbenoumltigt man in eigenstaumlndigen Skripten eineFunktion welche die gewuumlnschten Informationenauf dem Bildschirm ausgibt ndash dazu dient in Py-thon die print()-Funktion Nur in der interakti-ven Konsole kann auf diese Funktion in der Re-gel verzichtet werden (wie etwa bei den mathe-matischen Operationen oben)

usrbinenv python -- coding utf-8 --

print(Hallo Welt)

Listing 1 hello_worldpy

Diese Zeilen werden als hello_worldpy ge-speichert Die Datei hello_worldpy wird nunmit dem Befehl

$ chmod +x hello_worldpy

als ausfuumlhrbar markiert und schlieszliglich mit

$ hello_worldpy

oder

$ python hello_worldpy

gestartet Es erscheint folgende Meldung im Ter-minal

Hallo Welt

Das erste kleine Python-Skript ist funktionsfaumlhigBei der ersten Zeile handelt es sich um die soge-nannte Shebang-Zeile (siehe bdquoShebang ndash All derKramldquo freiesMagazin 112009 [3]) Hier wird fest-gelegt dass die Datei mit dem Python-Interpreterauszufuumlhren ist Die zweite Zeile informiert denPython-Interpreter uumlber die verwendete Zeichen-kodierung Diese beiden Zeilen sollten in allenPython-Dateien vorhanden sein Ohne Hinweisauf die Kodierung kann es zu Problemen mit Um-lauten in Zeichenketten kommen

Das erste bdquonuumltzlicheldquo ProgrammAls naumlchstes soll ein etwas ambitionierteres Pro-jekt in Angriff genommen werden Der Benutzersoll seinen Namen eingeben koumlnnen und diesendann in einer Box aus Gleichheitszeichen darge-stellt bekommen

usrbinenv python -- coding utf-8 --

name = raw_input(Hallo Wie heisst du )name_with_borders = = 0 =format(name)line = = len(name_with_borders)

print(line)print(name_with_borders)print(line)

Listing 2 your_namepy

Hier gibt es schon Neues zu sehen In der viertenZeile wird der Benutzer nach seinem Namen ge-fragt Die Funktion raw_input() gibt die als Pa-rameter uumlbergebene Zeichenkette auf dem Bild-schirm aus und wartet dann auf die Eingabe desBenutzers Diese wird nach dem Druumlcken vonEnter in der Variable name gespeichert (genau-er es wird ein Zeichenketten-Objekt erstellt aufdas die Variable verweist) Erst danach werdendie uumlbrigen Zeilen des Skriptes verarbeitet

Achtung Ab Python 3 heiszligt es input() undnicht mehr raw_input() Bis Python 3 ist vonder Verwendung von input() dringend abzura-ten weil die Benutzereingabe direkt vom Inter-preter ausgefuumlhrt wird ndash das ist in den allermeis-ten Faumlllen nicht erwuumlnscht

In Zeile 5 wird die Eingabe des Nutzers mitGleichheitszeichen umgeben Dazu wird die Zei-chenkette = 0 = erzeugt und die Zeichenfol-ge 0 mit Hilfe der format()-Methode durchden Inhalt der Variable name ersetzt In einem

spaumlteren Teil dieser Reihe wird aufdiese Art der Ersetzung genauer ein-gegangen werden In Zeile 6 wirdmit der Funktion len() die Laumln-ge des Benutzernamens inklusiveGleichheits- und Leerzeichen ermit-telt und dann die entsprechende An-zahl von Gleichheitszeichen ausge-geben Dazu wird hier der -Operatorbenutzt ndash auch Zeichenketten lassensich in Python also bdquomultiplizierenldquoSo entsteht eine Folge von Gleich-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 3

PROGRAMMIERUNG

heitszeichen die so lang ist wie die Zeichenkettename_with_borders

In Zeile 9 wird die in Zeile 5 erstellte Zeichenket-te auf dem Bildschirm ausgegeben Zeilen 8 und10 geben jeweils die in Zeile 6 erstellte bdquoLinieldquo aufdem Bildschirm aus Das Gesamtergebnis siehtdann wie folgt aus

Hallo Wie heisst du Margot=========== Margot ===========

ZeichenkettenJetzt wurden bereits erste Erfahrungen mit Zei-chenketten gesammelt Diese sollen hier vertieftwerden Zeichenketten muumlssen immer von An-fuumlhrungszeichen umschlossen werden Moumlglichsind einfache und doppelte Anfuumlhrungszeichen

name = Berndname = Bernd

Es gibt keinen Unterschied zwischen diesen bei-den Varianten ndash die Verwendung ist letztlichGeschmackssache Allerdings sollte darauf ge-achtet werden dass innerhalb einer Zeichenket-te keine aumluszligeren Anfuumlhrungsstriche vorkommenkoumlnnen

message = Ich heisse Bernd

fuumlhrt also zu einem Fehler Stattdessen kann insolchen Faumlllen der jeweils andere Anfuumlhrungs-strich genutzt werden

message = Ich heisse Bernd

oder

message = Ich heisse Bernd

In Python gibt es auszligerdem aber noch dreifacheAnfuumlhrungsstriche ( oder rsquorsquorsquo) Innerhalb vondreifachen Anfuumlhrungsstrichen kann man die ein-fachen und doppelten Anfuumlhrungszeichen nachBelieben verwenden und sogar Zeilenumbruumlchesind moumlglich

print(Hier kann ich und nach Belieben einsetzenAusserdem sind sogar Zeilenumbrueche moeglich)

Wie in vielen anderen Sprachen gibt es in Py-thon natuumlrlich auch die Moumlglichkeit Zeichenket-ten durch Voranstellen eines zu bdquoescapenldquo d hso zu markieren dass der Interpreter nicht dar-uumlber stolpert Das fehlerhafte Beispiel von obensaumlhe mit Escape-Zeichen dann so aus

message = Ich heisse Bernd

Mit dem Escape-Zeichen lassen sich auch Zei-lenumbruumlche in Zeichenketten mit einfachen unddoppelten Anfuumlhrungsstrichen erzwingen ndash dieswaumlre sonst nicht moumlglich

gtgtgt print(Ein nZeilenumbruch)EinZeilenumbruch

In der Python-Dokumentation finden sich weitereEscape-Sequenzen [4]

Zeichenketten naumlher betrachtetZeichenketten in Python gehoumlren ndash wie auch Zah-len ndash zu den unveraumlnderbaren Datentypen JedeVeraumlnderung an einer Zeichenkette liefert immereine neue Zeichenkette zuruumlck

gtgtgt text1 = HALLOgtgtgt text2 = text1lower()gtgtgt print(text1)HALLOgtgtgt print(text2)hallo

Hier wurden die Groszlig-buchstaben mit derMethode lower() in

Kleinbuchstaben bdquoumgewandeltldquo Tatsaumlchlichbleibt text1 davon aber unberuumlhrt stattdessenwird eine voumlllig neue Zeichenkette erzeugt Na-tuumlrlich ist es aber moumlglich eine Variable direktneu zuzuweisen so dass die Unveraumlnderbarkeitvon Strings in der Praxis kaum Bedeutung hat

gtgtgt text = HALLOgtgtgt text = textlower()gtgtgt print(text)hallo

Hier wird zunaumlchst der Variable text die Zeichen-kette HALLO zugewiesen Durch die Methodelower() wird eine neue Zeichenkette mit Klein-buchstaben erstellt Nun zeigt die Variable textauf die neu erstellte Zeichenkette Da jetzt kei-ne Variable mehr auf die alte Zeichenkette zeigtwird diese bei Zeiten automatisch aus dem Spei-cher geloumlscht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 4

PROGRAMMIERUNG

Analog zu lower() gibt es mit upper() eineMethode die eine Zeichenkette mit ausschlieszlig-lich Groszligbuchstaben erzeugt Mit swapcase()werden kleine Buchstaben zu Groszligbuchstabenund umgekehrt Geradezu unerlaumlsslich ist diereplace()-Methode Sie ersetzt alle Vorkom-men einer gesuchten Zeichenfolge innerhalb ei-ner Zeichenkette

gtgtgt Ich finde Python doofreplacey(doof super)Ich finde Python super

In der Python-Dokumentation finden sich vieleweitere nuumltzliche Zeichenketten-Funktionen [5]

Richtig einruumlckenViele Programmiersprachen kennen bestimmteKontrollstrukturen die den Programmfluss in be-sonderer Weise beeinflussen Hier ein Beispiel inPseudocode

zaehler = 1solange zahler lt= 5 wiederholegib zaehler auf dem Bildschirm auserhoehe zaehler um 1gib fertig auf dem Bildschirm aus

Klar Hier wird der Zaumlhler von 1 bis 5 hoch-gezaumlhlt und jeweils auf dem Bildschirm ausge-geben Aber wie oft wird bdquofertigldquo auf den Bild-schirm geschrieben Es wird deutlich dass demInterpreterCompiler irgendwie mitgeteilt werdenmuss welche Information noch zur Kontrollstruk-tur gehoumlrt und wo der normale Programmflussfortgesetzt wird

Viele andere Programmiersprachen loumlsen dasProblem mit geschweiften Klammern die Anfangund Ende des auszufuumlhrenden Codeblocks mar-kieren In Python gibt es derartige Klammernnicht ndash zusammengehoumlrende Codebloumlcke muumls-sen gemeinsam eingeruumlckt werden

zaehler = 1solange zahler lt= 5 wiederhole

gib zaehler auf dem Bildschirm yauserhoehe zaehler um 1

gib fertig auf dem Bildschirm aus

So weiszlig der Python-Interpreter dass nur Zeilen3 und 4 zur Kontrollstruktur gehoumlren bdquofertigldquo wirdnur einmal auf dem Bildschirm ausgegeben Wauml-re auch Zeile 5 eingeruumlckt wuumlrde bdquofertigldquo eben-falls fuumlnfmal ausgegeben

Das Einruumlcken ist eine Besonderheit von Py-thon und einigen wenigen anderen Sprachen diedas Lesen des Quelltextes vereinfachen soll DerBenutzer wird gezwungen sinnvoll einzuruumlckenWie viel eingeruumlckt wird und ob es mit Leerzei-chen oder Tabulatoren geschieht bleibt dem Be-nutzer uumlberlassen ndash es muss nur einheitlich seinSobald Leerzeichen und Tabulatoren gemischtwerden oder an einer Stelle mit vier Leerzeicheneingeruumlckt wird an anderer aber mit drei kommtes zu Problemen und das Programm wird sehrwahrscheinlich nicht richtig ausgefuumlhrt werdenAllgemein wird das Einruumlcken mit vier Leerzei-chen empfohlen Fast jeder gaumlngige Texteditorist heute in der Lage statt Tabulatoren Leerzei-

chen einzufuumlgen so dass dem Benutzer durchdie Verwendung von Leerzeichen keinerlei Nach-teile entstehen

for-SchleifeZuletzt soll hier nun die for-Schleife besprochenwerden Mit dieser Schleife kann man beliebigeAnweisungen beliebig oft ausfuumlhren lassen Mitfolgendem einfachen Beispiel werden zehn Zah-len nacheinander ausgegeben

usrbinenv python -- coding utf-8 --

for i in range(0 10)print(i)

print(Fertig)

Listing 3 for_schleifepy

Die Ausgabe des obigen Skriptes sieht wie folgtaus

0123456789Fertig

Was passiert in dem obigen Beispiel genauDie Funktion range() liefert (vereinfacht gesagt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 5

PROGRAMMIERUNG

es gibt hier Unterschiede in den verschiedenenPython-Versionen) eine Liste von 0 bis 9 zuruumlckDie Anweisung for i in range(0 10) laumlsstsich umgangssprachlich uumlbersetzen mit bdquoFuumlr je-des Element i in der Liste der Zahlen von 0 bisausschlieszliglich 10 mache ldquo

Die Liste der Zahlen von 0 bis 9 wird also schritt-weise durchlaufen Zunaumlchst wird i der Wert 0zugewiesen und dann die Anweisung print(i)ausgefuumlhrt Danach wird i der Wert 1 zugewie-sen und erneut der Anweisungsblock ausgefuumlhrtetc Man nennt dieses Vorgehen auch bdquoIterationldquoHier wird also uumlber die von range() erzeugte Lis-te bdquoiteriertldquo

An diesem Beispiel wird auch deutlich warumrichtiges Einruumlcken so wichtig ist Haumltte man die

Zeile print(Fertig) in Zeile 6 auch einge-ruumlckt waumlre diese Anweisung bei jedem Schlei-fendurchlauf ausgefuumlhrt worden ndash und nicht erstnach dem Durchlaufen der Schleife

Mit der for-Schleife endet der erste Teil der Ein-fuumlhrung in Python Im naumlchsten Teil werden dannif- und while-Bloumlcke sowie Listen besprochen

LINKS

[1] httpdocspythonorg[2] httpwwwpythonorgdevpepspep-0238[3] httpwwwfreiesmagazindefreiesMagazin-2009-

11[4] httpwwwpythonorgdoccurrentreference

lexical_analysishtmligrammar-token-escapeseq

[5] httpdocspythonorglibrarystdtypeshtmlstring-methods

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoBoyfriendldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom539

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 6

PROGRAMMIERUNG

Python-Programmierung Teil 2 ndash Tiefere Einblicke von Daniel Noumlgel

Im vorherigen Teil der Python-ReihebdquoPython-Programmierung Teil 1 ndash HalloWeltldquo auf Seite 2 wurden erste Erfahrun-

gen mit mathematischen Operatoren Zei-chenketten und for-Schleifen gesammelt Imzweiten Teil sollen nun besonders Listen inPython betrachtet werden Mit if und whilewerden aber auch zwei weitere Kontrollstruk-turen vorgestellt

Korrekturen und ErgaumlnzungenIm ersten Teil wurde bereits angesprochen dassmanche Spracheigenschaften von Python sichab Version 3x geaumlndert haben Dazu gehoumlreninsbesondere die Zeichenketten Erst ab 3x ar-beitet Python immer mit Unicode-ZeichenkettenDavor muss die Verwendung von Unicode bei derErstellung von Zeichenketten erzwungen werdenUnterbleibt dies koumlnnen schnell schwer zu ermit-telnde Probleme auftreten Auch Zeichenkettenaus Dateien und anderen Quellen sollten so fruumlhwie moumlglich in Unicode umgewandelt werden AbPython 3 muss sich der Entwickler darum nichtmehr kuumlmmern

Eine Unicode-Zeichenkette wird in Python 2xdurch das Voranstellen eines u vor die Zeichen-kette oder den Aufruf der Funktion unicode() er-stellt [1] [2] [3]

uIch bin ein Unicode-Stringunicode(Auch ich werde eine yUnicode-Zeichenkette)

Bei Python-Versionen lt 3 bin ich yein normaler Byte-String

Ein weiterer Unterschied zu Python 3x der imletzten Teil verschwiegen wurde ist die Verwen-dung von print Erst ab Python 3 wird print alsFunktion verwendet und muss wie folgt aufgeru-fen werden

gtgtgt print(Hallo Welt)

Vor Python 3 wurde print als Statement imple-mentiert

gtgtgt print Hallo Welt

Hinweis Wie schon im ersten Teil verein-bart werden Zeilen die mit gtgtgt beginnendirekt in die interaktive Konsole von Python

Eine Auswahl von Operatoren in PythonOperator Typ Funktion+ - Mathematisch Addition Subtraktion Multiplikation

Division Mathematisch Potenzierunglt gt lt= gt= Vergleich kleiner als groumlszliger als kleiner als

oder gleich groumlszliger als oder gleich== Vergleich gleich= Vergleich ungleich= Zuweisung weist einen Wert zuin Listen-Operator

Mitgliedschaftstesttestet ob der rechte Operand Mit-glied im linken Operanden ist

and Bool Operator Konjunktion logisches Undor Bool Operator Disjunktion logisches Odernot Bool Operator Negation logische Verneinung

eingegeben ndash dann natuumlrlich ohnedie spitzen Klammern

Die genauen Unterschiede sollenhier weiter keine Rolle spielenWichtig ist nur Nutzer von Python3x verwenden print als Funktion(mit Klammern) Nutzer von Py-thon 2x verwenden print ohneKlammern

Da heute noch zumeist Python 2xverwendet wird und viele Bibliothe-ken fuumlr Python 3x noch nicht an-gepasst wurden werden ab die-sem zweiten Teil die hier genann-

ten Ergaumlnzungen beruumlcksichtigt Allen Zeichen-ketten wird von nun an also ein u vorangestelltum Unicode-Zeichenketten zu erzeugen Benut-zereingaben werden im Folgenden mit der Funk-tion unicode() ebenfalls in Unicode umgewan-delt Nutzer von Python 3x muumlssen das voran-gestellte u und die Funktion unicode() jeweilsauslassen ndash in 3x wird ja ohnehin immer mit Uni-code gearbeitet

OperatorenBevor nun in den naumlchsten Abschnitten if- undwhile-Bloumlcke behandelt werden sollen zuerst ei-nige Operatoren besprochen werden Operato-ren sind ndash vereinfacht gesagt ndash (mathematische)Vorschriften durch die aus zwei Objekten einneues Objekt gebildet wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 7

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

heitszeichen die so lang ist wie die Zeichenkettename_with_borders

In Zeile 9 wird die in Zeile 5 erstellte Zeichenket-te auf dem Bildschirm ausgegeben Zeilen 8 und10 geben jeweils die in Zeile 6 erstellte bdquoLinieldquo aufdem Bildschirm aus Das Gesamtergebnis siehtdann wie folgt aus

Hallo Wie heisst du Margot=========== Margot ===========

ZeichenkettenJetzt wurden bereits erste Erfahrungen mit Zei-chenketten gesammelt Diese sollen hier vertieftwerden Zeichenketten muumlssen immer von An-fuumlhrungszeichen umschlossen werden Moumlglichsind einfache und doppelte Anfuumlhrungszeichen

name = Berndname = Bernd

Es gibt keinen Unterschied zwischen diesen bei-den Varianten ndash die Verwendung ist letztlichGeschmackssache Allerdings sollte darauf ge-achtet werden dass innerhalb einer Zeichenket-te keine aumluszligeren Anfuumlhrungsstriche vorkommenkoumlnnen

message = Ich heisse Bernd

fuumlhrt also zu einem Fehler Stattdessen kann insolchen Faumlllen der jeweils andere Anfuumlhrungs-strich genutzt werden

message = Ich heisse Bernd

oder

message = Ich heisse Bernd

In Python gibt es auszligerdem aber noch dreifacheAnfuumlhrungsstriche ( oder rsquorsquorsquo) Innerhalb vondreifachen Anfuumlhrungsstrichen kann man die ein-fachen und doppelten Anfuumlhrungszeichen nachBelieben verwenden und sogar Zeilenumbruumlchesind moumlglich

print(Hier kann ich und nach Belieben einsetzenAusserdem sind sogar Zeilenumbrueche moeglich)

Wie in vielen anderen Sprachen gibt es in Py-thon natuumlrlich auch die Moumlglichkeit Zeichenket-ten durch Voranstellen eines zu bdquoescapenldquo d hso zu markieren dass der Interpreter nicht dar-uumlber stolpert Das fehlerhafte Beispiel von obensaumlhe mit Escape-Zeichen dann so aus

message = Ich heisse Bernd

Mit dem Escape-Zeichen lassen sich auch Zei-lenumbruumlche in Zeichenketten mit einfachen unddoppelten Anfuumlhrungsstrichen erzwingen ndash dieswaumlre sonst nicht moumlglich

gtgtgt print(Ein nZeilenumbruch)EinZeilenumbruch

In der Python-Dokumentation finden sich weitereEscape-Sequenzen [4]

Zeichenketten naumlher betrachtetZeichenketten in Python gehoumlren ndash wie auch Zah-len ndash zu den unveraumlnderbaren Datentypen JedeVeraumlnderung an einer Zeichenkette liefert immereine neue Zeichenkette zuruumlck

gtgtgt text1 = HALLOgtgtgt text2 = text1lower()gtgtgt print(text1)HALLOgtgtgt print(text2)hallo

Hier wurden die Groszlig-buchstaben mit derMethode lower() in

Kleinbuchstaben bdquoumgewandeltldquo Tatsaumlchlichbleibt text1 davon aber unberuumlhrt stattdessenwird eine voumlllig neue Zeichenkette erzeugt Na-tuumlrlich ist es aber moumlglich eine Variable direktneu zuzuweisen so dass die Unveraumlnderbarkeitvon Strings in der Praxis kaum Bedeutung hat

gtgtgt text = HALLOgtgtgt text = textlower()gtgtgt print(text)hallo

Hier wird zunaumlchst der Variable text die Zeichen-kette HALLO zugewiesen Durch die Methodelower() wird eine neue Zeichenkette mit Klein-buchstaben erstellt Nun zeigt die Variable textauf die neu erstellte Zeichenkette Da jetzt kei-ne Variable mehr auf die alte Zeichenkette zeigtwird diese bei Zeiten automatisch aus dem Spei-cher geloumlscht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 4

PROGRAMMIERUNG

Analog zu lower() gibt es mit upper() eineMethode die eine Zeichenkette mit ausschlieszlig-lich Groszligbuchstaben erzeugt Mit swapcase()werden kleine Buchstaben zu Groszligbuchstabenund umgekehrt Geradezu unerlaumlsslich ist diereplace()-Methode Sie ersetzt alle Vorkom-men einer gesuchten Zeichenfolge innerhalb ei-ner Zeichenkette

gtgtgt Ich finde Python doofreplacey(doof super)Ich finde Python super

In der Python-Dokumentation finden sich vieleweitere nuumltzliche Zeichenketten-Funktionen [5]

Richtig einruumlckenViele Programmiersprachen kennen bestimmteKontrollstrukturen die den Programmfluss in be-sonderer Weise beeinflussen Hier ein Beispiel inPseudocode

zaehler = 1solange zahler lt= 5 wiederholegib zaehler auf dem Bildschirm auserhoehe zaehler um 1gib fertig auf dem Bildschirm aus

Klar Hier wird der Zaumlhler von 1 bis 5 hoch-gezaumlhlt und jeweils auf dem Bildschirm ausge-geben Aber wie oft wird bdquofertigldquo auf den Bild-schirm geschrieben Es wird deutlich dass demInterpreterCompiler irgendwie mitgeteilt werdenmuss welche Information noch zur Kontrollstruk-tur gehoumlrt und wo der normale Programmflussfortgesetzt wird

Viele andere Programmiersprachen loumlsen dasProblem mit geschweiften Klammern die Anfangund Ende des auszufuumlhrenden Codeblocks mar-kieren In Python gibt es derartige Klammernnicht ndash zusammengehoumlrende Codebloumlcke muumls-sen gemeinsam eingeruumlckt werden

zaehler = 1solange zahler lt= 5 wiederhole

gib zaehler auf dem Bildschirm yauserhoehe zaehler um 1

gib fertig auf dem Bildschirm aus

So weiszlig der Python-Interpreter dass nur Zeilen3 und 4 zur Kontrollstruktur gehoumlren bdquofertigldquo wirdnur einmal auf dem Bildschirm ausgegeben Wauml-re auch Zeile 5 eingeruumlckt wuumlrde bdquofertigldquo eben-falls fuumlnfmal ausgegeben

Das Einruumlcken ist eine Besonderheit von Py-thon und einigen wenigen anderen Sprachen diedas Lesen des Quelltextes vereinfachen soll DerBenutzer wird gezwungen sinnvoll einzuruumlckenWie viel eingeruumlckt wird und ob es mit Leerzei-chen oder Tabulatoren geschieht bleibt dem Be-nutzer uumlberlassen ndash es muss nur einheitlich seinSobald Leerzeichen und Tabulatoren gemischtwerden oder an einer Stelle mit vier Leerzeicheneingeruumlckt wird an anderer aber mit drei kommtes zu Problemen und das Programm wird sehrwahrscheinlich nicht richtig ausgefuumlhrt werdenAllgemein wird das Einruumlcken mit vier Leerzei-chen empfohlen Fast jeder gaumlngige Texteditorist heute in der Lage statt Tabulatoren Leerzei-

chen einzufuumlgen so dass dem Benutzer durchdie Verwendung von Leerzeichen keinerlei Nach-teile entstehen

for-SchleifeZuletzt soll hier nun die for-Schleife besprochenwerden Mit dieser Schleife kann man beliebigeAnweisungen beliebig oft ausfuumlhren lassen Mitfolgendem einfachen Beispiel werden zehn Zah-len nacheinander ausgegeben

usrbinenv python -- coding utf-8 --

for i in range(0 10)print(i)

print(Fertig)

Listing 3 for_schleifepy

Die Ausgabe des obigen Skriptes sieht wie folgtaus

0123456789Fertig

Was passiert in dem obigen Beispiel genauDie Funktion range() liefert (vereinfacht gesagt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 5

PROGRAMMIERUNG

es gibt hier Unterschiede in den verschiedenenPython-Versionen) eine Liste von 0 bis 9 zuruumlckDie Anweisung for i in range(0 10) laumlsstsich umgangssprachlich uumlbersetzen mit bdquoFuumlr je-des Element i in der Liste der Zahlen von 0 bisausschlieszliglich 10 mache ldquo

Die Liste der Zahlen von 0 bis 9 wird also schritt-weise durchlaufen Zunaumlchst wird i der Wert 0zugewiesen und dann die Anweisung print(i)ausgefuumlhrt Danach wird i der Wert 1 zugewie-sen und erneut der Anweisungsblock ausgefuumlhrtetc Man nennt dieses Vorgehen auch bdquoIterationldquoHier wird also uumlber die von range() erzeugte Lis-te bdquoiteriertldquo

An diesem Beispiel wird auch deutlich warumrichtiges Einruumlcken so wichtig ist Haumltte man die

Zeile print(Fertig) in Zeile 6 auch einge-ruumlckt waumlre diese Anweisung bei jedem Schlei-fendurchlauf ausgefuumlhrt worden ndash und nicht erstnach dem Durchlaufen der Schleife

Mit der for-Schleife endet der erste Teil der Ein-fuumlhrung in Python Im naumlchsten Teil werden dannif- und while-Bloumlcke sowie Listen besprochen

LINKS

[1] httpdocspythonorg[2] httpwwwpythonorgdevpepspep-0238[3] httpwwwfreiesmagazindefreiesMagazin-2009-

11[4] httpwwwpythonorgdoccurrentreference

lexical_analysishtmligrammar-token-escapeseq

[5] httpdocspythonorglibrarystdtypeshtmlstring-methods

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoBoyfriendldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom539

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 6

PROGRAMMIERUNG

Python-Programmierung Teil 2 ndash Tiefere Einblicke von Daniel Noumlgel

Im vorherigen Teil der Python-ReihebdquoPython-Programmierung Teil 1 ndash HalloWeltldquo auf Seite 2 wurden erste Erfahrun-

gen mit mathematischen Operatoren Zei-chenketten und for-Schleifen gesammelt Imzweiten Teil sollen nun besonders Listen inPython betrachtet werden Mit if und whilewerden aber auch zwei weitere Kontrollstruk-turen vorgestellt

Korrekturen und ErgaumlnzungenIm ersten Teil wurde bereits angesprochen dassmanche Spracheigenschaften von Python sichab Version 3x geaumlndert haben Dazu gehoumlreninsbesondere die Zeichenketten Erst ab 3x ar-beitet Python immer mit Unicode-ZeichenkettenDavor muss die Verwendung von Unicode bei derErstellung von Zeichenketten erzwungen werdenUnterbleibt dies koumlnnen schnell schwer zu ermit-telnde Probleme auftreten Auch Zeichenkettenaus Dateien und anderen Quellen sollten so fruumlhwie moumlglich in Unicode umgewandelt werden AbPython 3 muss sich der Entwickler darum nichtmehr kuumlmmern

Eine Unicode-Zeichenkette wird in Python 2xdurch das Voranstellen eines u vor die Zeichen-kette oder den Aufruf der Funktion unicode() er-stellt [1] [2] [3]

uIch bin ein Unicode-Stringunicode(Auch ich werde eine yUnicode-Zeichenkette)

Bei Python-Versionen lt 3 bin ich yein normaler Byte-String

Ein weiterer Unterschied zu Python 3x der imletzten Teil verschwiegen wurde ist die Verwen-dung von print Erst ab Python 3 wird print alsFunktion verwendet und muss wie folgt aufgeru-fen werden

gtgtgt print(Hallo Welt)

Vor Python 3 wurde print als Statement imple-mentiert

gtgtgt print Hallo Welt

Hinweis Wie schon im ersten Teil verein-bart werden Zeilen die mit gtgtgt beginnendirekt in die interaktive Konsole von Python

Eine Auswahl von Operatoren in PythonOperator Typ Funktion+ - Mathematisch Addition Subtraktion Multiplikation

Division Mathematisch Potenzierunglt gt lt= gt= Vergleich kleiner als groumlszliger als kleiner als

oder gleich groumlszliger als oder gleich== Vergleich gleich= Vergleich ungleich= Zuweisung weist einen Wert zuin Listen-Operator

Mitgliedschaftstesttestet ob der rechte Operand Mit-glied im linken Operanden ist

and Bool Operator Konjunktion logisches Undor Bool Operator Disjunktion logisches Odernot Bool Operator Negation logische Verneinung

eingegeben ndash dann natuumlrlich ohnedie spitzen Klammern

Die genauen Unterschiede sollenhier weiter keine Rolle spielenWichtig ist nur Nutzer von Python3x verwenden print als Funktion(mit Klammern) Nutzer von Py-thon 2x verwenden print ohneKlammern

Da heute noch zumeist Python 2xverwendet wird und viele Bibliothe-ken fuumlr Python 3x noch nicht an-gepasst wurden werden ab die-sem zweiten Teil die hier genann-

ten Ergaumlnzungen beruumlcksichtigt Allen Zeichen-ketten wird von nun an also ein u vorangestelltum Unicode-Zeichenketten zu erzeugen Benut-zereingaben werden im Folgenden mit der Funk-tion unicode() ebenfalls in Unicode umgewan-delt Nutzer von Python 3x muumlssen das voran-gestellte u und die Funktion unicode() jeweilsauslassen ndash in 3x wird ja ohnehin immer mit Uni-code gearbeitet

OperatorenBevor nun in den naumlchsten Abschnitten if- undwhile-Bloumlcke behandelt werden sollen zuerst ei-nige Operatoren besprochen werden Operato-ren sind ndash vereinfacht gesagt ndash (mathematische)Vorschriften durch die aus zwei Objekten einneues Objekt gebildet wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 7

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Analog zu lower() gibt es mit upper() eineMethode die eine Zeichenkette mit ausschlieszlig-lich Groszligbuchstaben erzeugt Mit swapcase()werden kleine Buchstaben zu Groszligbuchstabenund umgekehrt Geradezu unerlaumlsslich ist diereplace()-Methode Sie ersetzt alle Vorkom-men einer gesuchten Zeichenfolge innerhalb ei-ner Zeichenkette

gtgtgt Ich finde Python doofreplacey(doof super)Ich finde Python super

In der Python-Dokumentation finden sich vieleweitere nuumltzliche Zeichenketten-Funktionen [5]

Richtig einruumlckenViele Programmiersprachen kennen bestimmteKontrollstrukturen die den Programmfluss in be-sonderer Weise beeinflussen Hier ein Beispiel inPseudocode

zaehler = 1solange zahler lt= 5 wiederholegib zaehler auf dem Bildschirm auserhoehe zaehler um 1gib fertig auf dem Bildschirm aus

Klar Hier wird der Zaumlhler von 1 bis 5 hoch-gezaumlhlt und jeweils auf dem Bildschirm ausge-geben Aber wie oft wird bdquofertigldquo auf den Bild-schirm geschrieben Es wird deutlich dass demInterpreterCompiler irgendwie mitgeteilt werdenmuss welche Information noch zur Kontrollstruk-tur gehoumlrt und wo der normale Programmflussfortgesetzt wird

Viele andere Programmiersprachen loumlsen dasProblem mit geschweiften Klammern die Anfangund Ende des auszufuumlhrenden Codeblocks mar-kieren In Python gibt es derartige Klammernnicht ndash zusammengehoumlrende Codebloumlcke muumls-sen gemeinsam eingeruumlckt werden

zaehler = 1solange zahler lt= 5 wiederhole

gib zaehler auf dem Bildschirm yauserhoehe zaehler um 1

gib fertig auf dem Bildschirm aus

So weiszlig der Python-Interpreter dass nur Zeilen3 und 4 zur Kontrollstruktur gehoumlren bdquofertigldquo wirdnur einmal auf dem Bildschirm ausgegeben Wauml-re auch Zeile 5 eingeruumlckt wuumlrde bdquofertigldquo eben-falls fuumlnfmal ausgegeben

Das Einruumlcken ist eine Besonderheit von Py-thon und einigen wenigen anderen Sprachen diedas Lesen des Quelltextes vereinfachen soll DerBenutzer wird gezwungen sinnvoll einzuruumlckenWie viel eingeruumlckt wird und ob es mit Leerzei-chen oder Tabulatoren geschieht bleibt dem Be-nutzer uumlberlassen ndash es muss nur einheitlich seinSobald Leerzeichen und Tabulatoren gemischtwerden oder an einer Stelle mit vier Leerzeicheneingeruumlckt wird an anderer aber mit drei kommtes zu Problemen und das Programm wird sehrwahrscheinlich nicht richtig ausgefuumlhrt werdenAllgemein wird das Einruumlcken mit vier Leerzei-chen empfohlen Fast jeder gaumlngige Texteditorist heute in der Lage statt Tabulatoren Leerzei-

chen einzufuumlgen so dass dem Benutzer durchdie Verwendung von Leerzeichen keinerlei Nach-teile entstehen

for-SchleifeZuletzt soll hier nun die for-Schleife besprochenwerden Mit dieser Schleife kann man beliebigeAnweisungen beliebig oft ausfuumlhren lassen Mitfolgendem einfachen Beispiel werden zehn Zah-len nacheinander ausgegeben

usrbinenv python -- coding utf-8 --

for i in range(0 10)print(i)

print(Fertig)

Listing 3 for_schleifepy

Die Ausgabe des obigen Skriptes sieht wie folgtaus

0123456789Fertig

Was passiert in dem obigen Beispiel genauDie Funktion range() liefert (vereinfacht gesagt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 5

PROGRAMMIERUNG

es gibt hier Unterschiede in den verschiedenenPython-Versionen) eine Liste von 0 bis 9 zuruumlckDie Anweisung for i in range(0 10) laumlsstsich umgangssprachlich uumlbersetzen mit bdquoFuumlr je-des Element i in der Liste der Zahlen von 0 bisausschlieszliglich 10 mache ldquo

Die Liste der Zahlen von 0 bis 9 wird also schritt-weise durchlaufen Zunaumlchst wird i der Wert 0zugewiesen und dann die Anweisung print(i)ausgefuumlhrt Danach wird i der Wert 1 zugewie-sen und erneut der Anweisungsblock ausgefuumlhrtetc Man nennt dieses Vorgehen auch bdquoIterationldquoHier wird also uumlber die von range() erzeugte Lis-te bdquoiteriertldquo

An diesem Beispiel wird auch deutlich warumrichtiges Einruumlcken so wichtig ist Haumltte man die

Zeile print(Fertig) in Zeile 6 auch einge-ruumlckt waumlre diese Anweisung bei jedem Schlei-fendurchlauf ausgefuumlhrt worden ndash und nicht erstnach dem Durchlaufen der Schleife

Mit der for-Schleife endet der erste Teil der Ein-fuumlhrung in Python Im naumlchsten Teil werden dannif- und while-Bloumlcke sowie Listen besprochen

LINKS

[1] httpdocspythonorg[2] httpwwwpythonorgdevpepspep-0238[3] httpwwwfreiesmagazindefreiesMagazin-2009-

11[4] httpwwwpythonorgdoccurrentreference

lexical_analysishtmligrammar-token-escapeseq

[5] httpdocspythonorglibrarystdtypeshtmlstring-methods

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoBoyfriendldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom539

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 6

PROGRAMMIERUNG

Python-Programmierung Teil 2 ndash Tiefere Einblicke von Daniel Noumlgel

Im vorherigen Teil der Python-ReihebdquoPython-Programmierung Teil 1 ndash HalloWeltldquo auf Seite 2 wurden erste Erfahrun-

gen mit mathematischen Operatoren Zei-chenketten und for-Schleifen gesammelt Imzweiten Teil sollen nun besonders Listen inPython betrachtet werden Mit if und whilewerden aber auch zwei weitere Kontrollstruk-turen vorgestellt

Korrekturen und ErgaumlnzungenIm ersten Teil wurde bereits angesprochen dassmanche Spracheigenschaften von Python sichab Version 3x geaumlndert haben Dazu gehoumlreninsbesondere die Zeichenketten Erst ab 3x ar-beitet Python immer mit Unicode-ZeichenkettenDavor muss die Verwendung von Unicode bei derErstellung von Zeichenketten erzwungen werdenUnterbleibt dies koumlnnen schnell schwer zu ermit-telnde Probleme auftreten Auch Zeichenkettenaus Dateien und anderen Quellen sollten so fruumlhwie moumlglich in Unicode umgewandelt werden AbPython 3 muss sich der Entwickler darum nichtmehr kuumlmmern

Eine Unicode-Zeichenkette wird in Python 2xdurch das Voranstellen eines u vor die Zeichen-kette oder den Aufruf der Funktion unicode() er-stellt [1] [2] [3]

uIch bin ein Unicode-Stringunicode(Auch ich werde eine yUnicode-Zeichenkette)

Bei Python-Versionen lt 3 bin ich yein normaler Byte-String

Ein weiterer Unterschied zu Python 3x der imletzten Teil verschwiegen wurde ist die Verwen-dung von print Erst ab Python 3 wird print alsFunktion verwendet und muss wie folgt aufgeru-fen werden

gtgtgt print(Hallo Welt)

Vor Python 3 wurde print als Statement imple-mentiert

gtgtgt print Hallo Welt

Hinweis Wie schon im ersten Teil verein-bart werden Zeilen die mit gtgtgt beginnendirekt in die interaktive Konsole von Python

Eine Auswahl von Operatoren in PythonOperator Typ Funktion+ - Mathematisch Addition Subtraktion Multiplikation

Division Mathematisch Potenzierunglt gt lt= gt= Vergleich kleiner als groumlszliger als kleiner als

oder gleich groumlszliger als oder gleich== Vergleich gleich= Vergleich ungleich= Zuweisung weist einen Wert zuin Listen-Operator

Mitgliedschaftstesttestet ob der rechte Operand Mit-glied im linken Operanden ist

and Bool Operator Konjunktion logisches Undor Bool Operator Disjunktion logisches Odernot Bool Operator Negation logische Verneinung

eingegeben ndash dann natuumlrlich ohnedie spitzen Klammern

Die genauen Unterschiede sollenhier weiter keine Rolle spielenWichtig ist nur Nutzer von Python3x verwenden print als Funktion(mit Klammern) Nutzer von Py-thon 2x verwenden print ohneKlammern

Da heute noch zumeist Python 2xverwendet wird und viele Bibliothe-ken fuumlr Python 3x noch nicht an-gepasst wurden werden ab die-sem zweiten Teil die hier genann-

ten Ergaumlnzungen beruumlcksichtigt Allen Zeichen-ketten wird von nun an also ein u vorangestelltum Unicode-Zeichenketten zu erzeugen Benut-zereingaben werden im Folgenden mit der Funk-tion unicode() ebenfalls in Unicode umgewan-delt Nutzer von Python 3x muumlssen das voran-gestellte u und die Funktion unicode() jeweilsauslassen ndash in 3x wird ja ohnehin immer mit Uni-code gearbeitet

OperatorenBevor nun in den naumlchsten Abschnitten if- undwhile-Bloumlcke behandelt werden sollen zuerst ei-nige Operatoren besprochen werden Operato-ren sind ndash vereinfacht gesagt ndash (mathematische)Vorschriften durch die aus zwei Objekten einneues Objekt gebildet wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 7

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

es gibt hier Unterschiede in den verschiedenenPython-Versionen) eine Liste von 0 bis 9 zuruumlckDie Anweisung for i in range(0 10) laumlsstsich umgangssprachlich uumlbersetzen mit bdquoFuumlr je-des Element i in der Liste der Zahlen von 0 bisausschlieszliglich 10 mache ldquo

Die Liste der Zahlen von 0 bis 9 wird also schritt-weise durchlaufen Zunaumlchst wird i der Wert 0zugewiesen und dann die Anweisung print(i)ausgefuumlhrt Danach wird i der Wert 1 zugewie-sen und erneut der Anweisungsblock ausgefuumlhrtetc Man nennt dieses Vorgehen auch bdquoIterationldquoHier wird also uumlber die von range() erzeugte Lis-te bdquoiteriertldquo

An diesem Beispiel wird auch deutlich warumrichtiges Einruumlcken so wichtig ist Haumltte man die

Zeile print(Fertig) in Zeile 6 auch einge-ruumlckt waumlre diese Anweisung bei jedem Schlei-fendurchlauf ausgefuumlhrt worden ndash und nicht erstnach dem Durchlaufen der Schleife

Mit der for-Schleife endet der erste Teil der Ein-fuumlhrung in Python Im naumlchsten Teil werden dannif- und while-Bloumlcke sowie Listen besprochen

LINKS

[1] httpdocspythonorg[2] httpwwwpythonorgdevpepspep-0238[3] httpwwwfreiesmagazindefreiesMagazin-2009-

11[4] httpwwwpythonorgdoccurrentreference

lexical_analysishtmligrammar-token-escapeseq

[5] httpdocspythonorglibrarystdtypeshtmlstring-methods

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoBoyfriendldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom539

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 6

PROGRAMMIERUNG

Python-Programmierung Teil 2 ndash Tiefere Einblicke von Daniel Noumlgel

Im vorherigen Teil der Python-ReihebdquoPython-Programmierung Teil 1 ndash HalloWeltldquo auf Seite 2 wurden erste Erfahrun-

gen mit mathematischen Operatoren Zei-chenketten und for-Schleifen gesammelt Imzweiten Teil sollen nun besonders Listen inPython betrachtet werden Mit if und whilewerden aber auch zwei weitere Kontrollstruk-turen vorgestellt

Korrekturen und ErgaumlnzungenIm ersten Teil wurde bereits angesprochen dassmanche Spracheigenschaften von Python sichab Version 3x geaumlndert haben Dazu gehoumlreninsbesondere die Zeichenketten Erst ab 3x ar-beitet Python immer mit Unicode-ZeichenkettenDavor muss die Verwendung von Unicode bei derErstellung von Zeichenketten erzwungen werdenUnterbleibt dies koumlnnen schnell schwer zu ermit-telnde Probleme auftreten Auch Zeichenkettenaus Dateien und anderen Quellen sollten so fruumlhwie moumlglich in Unicode umgewandelt werden AbPython 3 muss sich der Entwickler darum nichtmehr kuumlmmern

Eine Unicode-Zeichenkette wird in Python 2xdurch das Voranstellen eines u vor die Zeichen-kette oder den Aufruf der Funktion unicode() er-stellt [1] [2] [3]

uIch bin ein Unicode-Stringunicode(Auch ich werde eine yUnicode-Zeichenkette)

Bei Python-Versionen lt 3 bin ich yein normaler Byte-String

Ein weiterer Unterschied zu Python 3x der imletzten Teil verschwiegen wurde ist die Verwen-dung von print Erst ab Python 3 wird print alsFunktion verwendet und muss wie folgt aufgeru-fen werden

gtgtgt print(Hallo Welt)

Vor Python 3 wurde print als Statement imple-mentiert

gtgtgt print Hallo Welt

Hinweis Wie schon im ersten Teil verein-bart werden Zeilen die mit gtgtgt beginnendirekt in die interaktive Konsole von Python

Eine Auswahl von Operatoren in PythonOperator Typ Funktion+ - Mathematisch Addition Subtraktion Multiplikation

Division Mathematisch Potenzierunglt gt lt= gt= Vergleich kleiner als groumlszliger als kleiner als

oder gleich groumlszliger als oder gleich== Vergleich gleich= Vergleich ungleich= Zuweisung weist einen Wert zuin Listen-Operator

Mitgliedschaftstesttestet ob der rechte Operand Mit-glied im linken Operanden ist

and Bool Operator Konjunktion logisches Undor Bool Operator Disjunktion logisches Odernot Bool Operator Negation logische Verneinung

eingegeben ndash dann natuumlrlich ohnedie spitzen Klammern

Die genauen Unterschiede sollenhier weiter keine Rolle spielenWichtig ist nur Nutzer von Python3x verwenden print als Funktion(mit Klammern) Nutzer von Py-thon 2x verwenden print ohneKlammern

Da heute noch zumeist Python 2xverwendet wird und viele Bibliothe-ken fuumlr Python 3x noch nicht an-gepasst wurden werden ab die-sem zweiten Teil die hier genann-

ten Ergaumlnzungen beruumlcksichtigt Allen Zeichen-ketten wird von nun an also ein u vorangestelltum Unicode-Zeichenketten zu erzeugen Benut-zereingaben werden im Folgenden mit der Funk-tion unicode() ebenfalls in Unicode umgewan-delt Nutzer von Python 3x muumlssen das voran-gestellte u und die Funktion unicode() jeweilsauslassen ndash in 3x wird ja ohnehin immer mit Uni-code gearbeitet

OperatorenBevor nun in den naumlchsten Abschnitten if- undwhile-Bloumlcke behandelt werden sollen zuerst ei-nige Operatoren besprochen werden Operato-ren sind ndash vereinfacht gesagt ndash (mathematische)Vorschriften durch die aus zwei Objekten einneues Objekt gebildet wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 7

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Python-Programmierung Teil 2 ndash Tiefere Einblicke von Daniel Noumlgel

Im vorherigen Teil der Python-ReihebdquoPython-Programmierung Teil 1 ndash HalloWeltldquo auf Seite 2 wurden erste Erfahrun-

gen mit mathematischen Operatoren Zei-chenketten und for-Schleifen gesammelt Imzweiten Teil sollen nun besonders Listen inPython betrachtet werden Mit if und whilewerden aber auch zwei weitere Kontrollstruk-turen vorgestellt

Korrekturen und ErgaumlnzungenIm ersten Teil wurde bereits angesprochen dassmanche Spracheigenschaften von Python sichab Version 3x geaumlndert haben Dazu gehoumlreninsbesondere die Zeichenketten Erst ab 3x ar-beitet Python immer mit Unicode-ZeichenkettenDavor muss die Verwendung von Unicode bei derErstellung von Zeichenketten erzwungen werdenUnterbleibt dies koumlnnen schnell schwer zu ermit-telnde Probleme auftreten Auch Zeichenkettenaus Dateien und anderen Quellen sollten so fruumlhwie moumlglich in Unicode umgewandelt werden AbPython 3 muss sich der Entwickler darum nichtmehr kuumlmmern

Eine Unicode-Zeichenkette wird in Python 2xdurch das Voranstellen eines u vor die Zeichen-kette oder den Aufruf der Funktion unicode() er-stellt [1] [2] [3]

uIch bin ein Unicode-Stringunicode(Auch ich werde eine yUnicode-Zeichenkette)

Bei Python-Versionen lt 3 bin ich yein normaler Byte-String

Ein weiterer Unterschied zu Python 3x der imletzten Teil verschwiegen wurde ist die Verwen-dung von print Erst ab Python 3 wird print alsFunktion verwendet und muss wie folgt aufgeru-fen werden

gtgtgt print(Hallo Welt)

Vor Python 3 wurde print als Statement imple-mentiert

gtgtgt print Hallo Welt

Hinweis Wie schon im ersten Teil verein-bart werden Zeilen die mit gtgtgt beginnendirekt in die interaktive Konsole von Python

Eine Auswahl von Operatoren in PythonOperator Typ Funktion+ - Mathematisch Addition Subtraktion Multiplikation

Division Mathematisch Potenzierunglt gt lt= gt= Vergleich kleiner als groumlszliger als kleiner als

oder gleich groumlszliger als oder gleich== Vergleich gleich= Vergleich ungleich= Zuweisung weist einen Wert zuin Listen-Operator

Mitgliedschaftstesttestet ob der rechte Operand Mit-glied im linken Operanden ist

and Bool Operator Konjunktion logisches Undor Bool Operator Disjunktion logisches Odernot Bool Operator Negation logische Verneinung

eingegeben ndash dann natuumlrlich ohnedie spitzen Klammern

Die genauen Unterschiede sollenhier weiter keine Rolle spielenWichtig ist nur Nutzer von Python3x verwenden print als Funktion(mit Klammern) Nutzer von Py-thon 2x verwenden print ohneKlammern

Da heute noch zumeist Python 2xverwendet wird und viele Bibliothe-ken fuumlr Python 3x noch nicht an-gepasst wurden werden ab die-sem zweiten Teil die hier genann-

ten Ergaumlnzungen beruumlcksichtigt Allen Zeichen-ketten wird von nun an also ein u vorangestelltum Unicode-Zeichenketten zu erzeugen Benut-zereingaben werden im Folgenden mit der Funk-tion unicode() ebenfalls in Unicode umgewan-delt Nutzer von Python 3x muumlssen das voran-gestellte u und die Funktion unicode() jeweilsauslassen ndash in 3x wird ja ohnehin immer mit Uni-code gearbeitet

OperatorenBevor nun in den naumlchsten Abschnitten if- undwhile-Bloumlcke behandelt werden sollen zuerst ei-nige Operatoren besprochen werden Operato-ren sind ndash vereinfacht gesagt ndash (mathematische)Vorschriften durch die aus zwei Objekten einneues Objekt gebildet wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 7

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Die uumlblichen mathematischen Operatoren sind si-cher ebenso bekannt wie die Vergleichsoperato-ren Fuumlr Verwunderung sorgt vielleicht der Divi-sionsoperator liefert bis Python 3 ganzzahligeErgebnisse wenn nicht explizit Flieszligkommazah-len dividiert werden Erst ab Python 3 gibt dieserOperator Flieszligkommazahlen zuruumlck wenn dasErgebnis keine natuumlrliche Zahl ist

Auch auf den Unterschied des Vergleichsopera-tors == und des Zuweisungsoperators = soll hin-gewiesen werden x == 3 liefert abhaumlngig von xentweder True oder False zuruumlck x = 3 dahin-gegen weist x den Wert 3 zu Gerade bei Anfaumln-gern ist das eine beliebte Fehlerquelle

Der in-Operator kommt bei allen iterierbaren Ob-jekten (also besonders Listen) zur Geltung Mitihm laumlsst sich in Erfahrung bringen ob ein be-stimmter Eintrag in einer Liste vorhanden ist

Die Booleschen Operatoren [4] and und or die-nen zur Verknuumlpfung mehrere WahrheitswerteDer Ausdruck 3 lt 5 and 3 lt 2 ist offensicht-lich falsch der Ausdruck 3 lt 5 or 3 lt 2 dahin-gegen wahr Der Operator not dreht einen Wahr-heitswert schlicht um Der Ausdruck 3 lt 5 andnot 3 lt 2 ist also ebenfalls wahr

Eine vollstaumlndige Uumlbersicht der Operatoren in Py-thon findet sich unter anderem im kostenlos ver-fuumlgbaren Buch bdquoA Byte of Pythonldquo [5]

if-Anweisungif-Bloumlcke bieten die Moumlglichkeit das Ausfuumlhreneines bestimmten Code-Teiles von einer oder

mehreren Bedingungen abhaumlngig zu machen

In dem Kopf des if-Blockes wird die Bedingungfuumlr die Ausfuumlhrung definiert also beispielsweise

1 number = 52 if number gt 33 print uZahl groesser als 3

Bei der Definition derartiger Bedingungen sindbesonders vergleichende Operatoren wichtig ImKopf eines if-Blockes koumlnnen ndash durch boole-sche Operatoren verknuumlpft ndash eine ganze Reihederartiger Vergleiche aneinandergereiht werden

1 number = 202 if number gt 10 and number lt 403 print uZahl liegt zwischen y

10 und 40

Durch den Operator and muumlssen beide Verglei-che wahr sein damit der if-Rumpf ausgefuumlhrtund die Meldung ausgegeben wird Verwendetman dahingegen den Operator or muss nur ei-ne der Bedingungen wahr sein

1 good_looking = False2 rich = True3 if good_looking == True or rich y== True

4 print uHeirate mich

Hier wird die Meldung bdquoHeirate michldquo ausgege-ben wenn die Variable good_looking oder dieVariable rich True ist (oder beide) In Zeile 3werden die Variablen dazu mit True verglichen

Dieser Vergleich mit True ist eigentlich immer un-noumltig Uumlblich und schoumlner zu lesen ist folgendeSchreibweise

1 if good_looking or rich2 print uHeirate mich

Am Ende dieses Abschnitt soll noch kurz auf dieMoumlglichkeit eingegangen werden mehrere Even-tualitaumlten mit if abzudecken

1 if number lt 102 print uKleiner 103 elif number lt 204 print uKleiner 205 else6 print uGroesser oder gleich y

20

Das Schluumlsselwort elif steht fuumlr else if undgelangt immer dann zur Ausfuumlhrung wenn dievorherige if-Bedingung nicht erfuumlllt war Mitelif koumlnnen ndash ebenso wie mit if ndash eine Vielzahlvon Bedingungen definiert werden

Waumlre number beispielsweise 3 waumlre die Bedin-gung in Zeile 1 wahr und Zeile 2 kaumlme zur Aus-fuumlhrung Waumlre number aber 11 waumlre die Bedin-gung in Zeile 1 nicht erfuumlllt und der Interpreterwuumlrde die Bedingung in Zeile 3 pruumlfen Da die-se in diesem Fall wahr waumlre kaumlme Zeile 4 zurAusfuumlhrung Waumlre number aber nun 40 und ent-sprechend keine der beiden Bedingungen wahrkaumlme Zeile 6 zur Ausfuumlhrung Das Schluumlsselwortelse ist also immer dann (und nur dann) vonBedeutung wenn keine der vorherigen if oderelif-Bedingungen erfuumlllt wurde

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 8

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

while-SchleifeEine weitere wichtige Kontrollstruktur in Pythonist die while-Schleife So lange die im Schleifen-kopf definierten Bedingungen wahr sind wird derSchleifenrumpf ausgefuumlhrt Ein sehr einfachesBeispiel ist folgende Endlosschleife

1 while True2 raw_input(uWie war Ihr Name y

noch gleich)

Da die Bedingung True immer wahr ist wird dieSchleife nie enden Durch die TastenkombinationStrg + C kann die Ausfuumlhrung des Programmsaber beendet werden

Sinnvoller ist eine derartige Schleife natuumlrlichwenn eine Abbruchbedingung definiert wirdDenkbar waumlre hier beispielsweise das Sammelnvon Namen bis der Benutzer das Programmdurch die Eingabe von exit beendet

1 names = []2 running = True3 while running4 user_input = unicode(y

raw_input(uGeben Sie einen yNamen ein oder exit zum yBeenden gt ))

5 if user_input == uexit6 running = False7 else8 namesappend(user_input)9 print uSie haben folgende Namen yeingegeben

10 print names

Wichtig ist hier die Funktion unicode() Sie wan-delt in Python 2x die Eingabe des Benutzers inUnicode um Da in Python 3x von Haus aus mitUnicode-Zeichenketten gearbeitet wird gibt esdiese Funktion dort nicht mehr

Hinweis Nutzer von Python 3 verwenden stattraw_input lediglich input

Zwischenfazit KontrollstrukturenBisher wurde folgende Kontrollstrukturen behan-delt if for und while Fuumlr diese Strukturen gilt

Jede Kontrollstruktur besteht aus einem Kopfder die Ausfuumlhrungsbedingungen definiert undeinem Rumpf der ausgefuumlhrt werden soll

Der Kopf einer Kontrollstruktur wird immer miteinem Doppelpunkt abgeschlossen

Der Rumpf einer Kontrollstruktur muss immerum eine Ebene eingeruumlckt werden

Die Einruumlckungen muumlssen immer gleichmaumlszligigsein

Vier verschiedene Namen werden eingegeben

Kontrollstrukturen koumlnnen natuumlrlich auch ver-schachtelt werden Folgendes Beispiel veran-schaulicht dies

1 if username == uBernd2 if password == uxy3 print uAlles ok4 else5 print uPassword falsch6 else7 print uBenutzername falsch

Der innere if-Block muss also insgesamt eineEbene eingeruumlckt werden ndash er gehoumlrt ja zumRumpf des aumluszligeren if-Blockes Der Rumpf desinneren if-Blockes muss um zwei Ebenen einge-ruumlckt werden

Jede Verschachtelungsebene muss also durchEinruumlckung von der vorherigen Ebene getrenntwerden

Weitere Informationen uumlber Kontrollstrukturen fin-den sich in der Python-Dokumentation [6]

ListenIn Teil 1 dieser Einfuumlhrung wur-de mit der Funktion range()eine Liste von 0 bis 9 gene-riert Hier soll nun abschlie-szligend naumlher auf Listen einge-gangen werden Bei Listen han-delt es sich um einen Datentypder beliebige andere Datenty-pen verwalten kann (sogar ge-mischt) ndash gewissermaszligen also

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 9

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

ein Aktenschrank fuumlr Zeichenketten Zahlen undalle moumlglichen anderen Objekte die in Pythonvorkommen (sogar Listen lassen sich in Listenablegen so dass verschachtelte Listen moumlglichsind) [7]

Listen werden in Python mit eckigen Klammern([ und ]) gekennzeichnet Sie sind sehr leicht zuerstellen

1 gtgtgt persons = []2 gtgtgt type(persons)3 lttype listgt4 gtgtgt persons = list()5 gtgtgt type(persons)6 lttype listgt7 gtgtgtpersons = [uPeter uHermanny uSimon]

In Zeile 1 wird eine leere Liste erstellt und anden Namen persons gebunden In Zeile 2 wirdmit der Funktion type() der Typ des Objekteswelches an persons gebunden ist ausgegebenWie erwartet handelt es sich dabei um eine Lis-te Zeile 4 zeigt die Erzeugung mittels der Funk-tion list() Das Ergebnis ist das gleiche In Zei-le 7 sieht man dass man in Python eine Listedirekt befuumlllen kann Es werden die drei Unicode-Zeichenketten Peter Hermann und Simon in dieListe eingetragen

Wie schon in Teil 1 gezeigt wurde laumlsst sichsehr einfach uumlber Listen iterieren Listen habenaber auch zusaumltzliche Methoden die sehr nuumltz-lich sein koumlnnen

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsappend(uHermann)4 gtgtgt persons5 [uPeter uHermann uSimon yuCarla uHermann]

6 gtgtgt personsremove(uHermann)7 gtgtgt persons8 [uPeter uSimon uCarla uyHermann]

Mit der Methode append() kann ein weiterer Ein-trag angehaumlngt werden wie man in den Zeilen2 und 3 sehen kann Zeile 5 zeigt das ErgebnisDie beiden Unicode-Objekte Carla und Hermanwurden in der Reihenfolge der append-Aufrufe andie Liste angefuumlgt

Analog dazu lassen sich Eintraumlge mit der Metho-de remove() entfernen (Zeile 6) Hierbei solltebeachtet werden dass jeweils nur das erste Vor-kommen von Hermann entfernt wird Gibt es meh-rere gleichlautende Eintraumlge muss remove()auch mehrfach ausgefuumlhrt werden etwa wiefolgt

1 gtgtgt persons = [uPeter uyHermann uHermann]

2 gtgtgt while uHermann in persons3 gtgtgt personsremove(uHermanny)

4 gtgtgt print persons5 [Peter]

Wichtig hierbei Da die Zeichenketten Hermann inder Liste Unicode-Objekte sind sollte auch alsSuch-Zeichenkette ein Unicode-Objekt angege-ben werden um Fehler zu vermeiden Wird ver-sucht einen Eintrag zu entfernen der gar nicht inder Liste vorhanden ist (etwa Heidi) kommt eszu einer Fehlermeldung ndash hier als Beispiel in derinteraktiven Python-Konsole

1 gtgtgt persons = [uPeter uyHermann uSimon]

2 gtgtgt personsappend(uCarla)3 gtgtgt personsremove(uHermann)4 gtgtgt print persons5 [uPeter uSimon uCarla]6 gtgtgt personsremove(uHeidi)7 Traceback (most recent call last)y

8 File ltstdingt line 1 in ltymodulegt

9 ValueError listremove(x) x notyin list

Nach den Veraumlnderungen der Liste in den Zei-len 1 bis 3 ist in Zeile 5 noch alles in Ord-nung Hermann wurde aus der Liste geloumlschtCarla hinzugefuumlgt Der Versuch Heidi zu ent-fernen scheitert Dieser Eintrag ist in der Listegar nicht vorhanden Die Zeilen 7-9 zeigen dieReaktion des Python-Interpreters darauf In ei-nem spaumlteren Teil dieser Reihe werden Python-Fehler (meist Exceptions genannt) naumlher behan-delt Hier soll zunaumlchst gezeigt werden wie vordem Loumlschen eines Eintrages uumlberpruumlft werdenkann ob er sich uumlberhaupt in der Liste befindet

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 10

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

if uHeidi in personspersonsremove(uHeidi)

Nun wird mit dem Operator in gepruumlft ob der Ein-trag Heidi uumlberhaupt in der Liste existiert Sehrschoumln zu sehen ist dabei wie intuitiv und natuumlr-lich Python sein kann

Listen-IndizesBeim Umgang mit Listen sollte man wissen dassPython die Listeneintraumlge mit einem sogenann-ten bdquoIndexldquo verwaltet Jedem Listeneintrag wirdmit 0 beginnend eine eindeutige Zahl zugewie-sen Der erste Eintrag wird also mit 0 angespro-chen der zweite Eintrag mit 1 usw So ist es sehrleicht auf einzelne Eintraumlge zuzugreifen

gtgtgt letters = [ua ub uc]gtgtgt letters[1]ub

Damit wird der zweite Listeneintrag ausgelesenndash der erste Listeneintrag hat ja den Index 0 Sollvon hinten gezaumlhlt werden wird einfach ein nega-tiver Index angegeben

gtgtgt letters[-3]ua

Weitere Listen-MethodenDie gerade besprochenen Indizes spielen auchbei bestimmten Methoden von Listen eine RolleSo gibt es mit insert() und pop() die Moumlglich-keit Eintraumlge an einer bestimmten Stelle der Lis-te einzufuumlgen oder zu entfernen

1 gtgtgt letters = [ua uc ue]2 gtgtgt lettersinsert(1 ub)3 gtgtgt letters4 [ua ub uc ue]5 gtgtgt lettersinsert(3 ud)6 gtgtgt letters7 [ua ub uc ud ue]8 gtgtgt letterspop()9 ue10 gtgtgt letters11 [ua ub uc ud]12 gtgtgt letterspop(2)13 uc14 gtgtgt letters15 [ua ub ud]

In Zeile 2 wird mit der insert()-Methode ban die richtige Stelle der Liste befoumlrdert in Zei-le 5 wird mit d analog verfahren Zu beachtenist hier dass der erste Parameter der insert()-Methode immer die gewuumlnschte Position im In-dex der Liste angibt (daher muss erneut von 0gezaumlhlt werden) der zweite Parameter beinhal-tet das einzufuumlgende Objekt pop() loumlscht dasletzte Element aus einer Liste und gibt dieses zu-ruumlck Alternativ kann auch ein bestimmter Eintragaus einer Liste geloumlscht werden ndash dazu wird derentsprechende Index als Parameter angegeben

Slicing

Sehr wichtig fuumlr Listen ist auch das Slicing ndash alsodas bdquoZerschneidenldquo Mit dem slicing-Operatorkoumlnnen einzelne Elemente oder Ausschnitte vonListen ausgelesen werden Der Operator siehtdabei wie folgt aus

[vonbis]

von steht dabei fuumlr den Eintrag der Liste bei demdas Zerschneiden beginnen soll ndash es wird von 0gezaumlhlt bis steht fuumlr den Listeneintrag vor demdas Zerschneiden endet

gtgtgt li = [ua ub uc ud uye]gtgtgt li[03][ua ub uc]gtgtgt li[25][uc ud ue]

Es ist auch moumlglich das Ende des Schnittes vomEnde der Liste aus zu definieren ndash indem ein ne-gatives Vorzeichen gewaumlhlt wird

gtgtgt li[0-1][ua ub uc ud]gtgtgt li[0-2][ua ub uc ]gtgtgt li[1-2][ub uc]

Abkuumlrzen erlaubtSoll der erste Schnitt gleich am Anfang der Lis-te gesetzt werden muss nicht nicht immer 0 alsStartpunkt gesetzt werden

gtgtgt li = [ua ub uc ud uye]gtgtgt li[3]

gibt wie gewuumlnscht [ua ub uc] zu-ruumlck Auch beim zweiten Schnitt kann abgekuumlrzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 11

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

werden Soll dieser hinter dem letzten Listenele-ment erfolgen wird ebenfalls keine Angabe ge-macht

gtgtgt li[2][uc ud ue]

Es ist nicht schwer zu erraten was der Ausdruck

gtgtgt li[]

folglich bewirken muss

Listen durch Slices veraumlndernBisher wurde nur lesend auf verschiedene Listen-Indizes zugegriffen Die Ursprungsliste wurde da-bei jedoch nie veraumlndert Mit dem Zuweisungs-operator lassen sich aber auch einzelne Indizesuumlberschreiben oder ganze Bereiche einfuumlgen

1 gtgtgt li = [ua ub uc]2 gtgtgt li[2] = ue3 gtgtgt li4 [ua ub ue]5 gtgtgt li[22] = [uc ud]6 gtgtgt li7 [ua ub uc ud ue]8 gtgtgt li[3] = [1 2 3]9 gtgtgt li10 [ua ub uc 1 2 3]

Hier wird zunaumlchst eine Liste mit den Buchsta-ben a bis c erstellt Der dritte Eintrag der Lis-te wird in Zeile 2 durch e ersetzt In Zeile 4 istzu sehen dass die Liste li dadurch veraumlndertwurde In Zeile 5 werden zwischen b und e zwei

weitere Listenelemente eingefuumlgt Durch den Sli-ce [22] wird der Schnitt direkt vor dem drit-ten Listenelement gesetzt (Index 2) so dass dieBuchstabenreihenfolge wieder stimmt In Zeile 8ist schlieszliglich zu sehen wie ein ganzer Slice derListe uumlberschrieben wird

Es ist sehr zu empfehlen das Slicing und die an-deren hier vorgestellten Methoden und Funktio-nen in der interaktiven Python-Konsole ein wenigzu erproben Auch die Python-Dokumentationkann wertvolle Hinweise zum Umgang mit Listenliefern [8]

Ein kleines BeispielDas folgende Beispiel setzt einige der hier erlern-ten Techniken ein

1 usrbinenv python2 coding utf-834 allowed_tries = 55 counter = 167 users = [uKarl uWilli uJoey]

8 passwords = [ukarl123 uywilli456 ujoe789]

910 while counter lt= allowed_tries11 username = unicode(raw_input(y

uBitte geben sie ihren yBenutzernamen ein ))

12 password = unicode(raw_input(yuBitte geben sie ihr yPasswort ein ))

1314 if not username in users15 print uDieser Benutzer y

existiert nicht16 else17 idx = usersindex(y

username)18 if passwords[idx] == y

password19 print uErfolgreich y

eingeloggt20 break21 else22 print uSie haben einy

falches Passwort yeingegeben

2324 counter += 12526 if counter gt allowed_tries27 print uSie haben es zu y

oft versucht

Listing 1 beispielpy

Hinweis Benutzer von Python 30 verwendenanstatt raw_input() schlicht input()

In den Zeilen 7 und 8 werden zwei Listen defi-niert users beinhaltet die verschiedenen Benut-zer passwords deren Passworte Dabei gehoumlrenimmer die Listeneintraumlge mit dem selben Index-Wert zusammen (also Karl und karl123 etc)

Die Schleife in diesem Beispiel wird houmlchstensfuumlnfmal ausgefuumlhrt ndash nach fuumlnf Durchlaumlufen hatcounter den Wert 6 so dass die Bedingung derwhile-Schleife nicht mehr wahr ist

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 12

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

In Zeile 14 wird gepruumlft ob der Benutzernamenicht in der Liste vorkommt ndash in diesem Fall wirddie Fehlermeldung in Zeile 15 ausgegeben undder Zaumlhler in Zeile 24 um 1 erhoumlht Anderen-falls (ab Zeile 16) wird zunaumlchst mit der Metho-de index() die Position des Benutzernamens inder Liste users ermittelt In Zeile 18 wird das da-zugehoumlrige Passwort mit dem vom Benutzer ein-gegebenen Passwort verglichen Stimmen beideuumlberein wird in Zeile 19 eine Meldung ausgege-ben und die Schleife in Zeile 20 mit dem neuenSchluumlsselwort break abgebrochen

Im naumlchsten Teil dieser Reihe wird auf einebesondere Art der Ersetzung in Zeichenketten(bdquoString Substitutionldquo) sowie Module und Funktio-nen eingegangen

LINKS

[1] httpdocspythonorghowtounicodehtml[2] httpwikipython-forumdeVon20Umlauten

20Unicode20und20Encodings[3] httpwikipythondeUser20Group20MC3

BCnchenaction=AttachFileampdo=viewamptarget=unicode-folienpdf

[4] httpdewikipediaorgwikiBoolesche_Algebra[5] httpabop-germanberliosdereadoperators

html[6] httpdocspythonorgpy3kreferencecompound_

stmtshtml[7] httpdocspythonorgfaqdesignhtmlhow-are-

lists-implemented[8] httpdocspythonorgtutorialdatastructures

htmlmore-on-lists

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoStill No Sleepldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom776

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 13

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Python-Programmierung Teil 3 ndash Funktionen und Module von Daniel Noumlgel

Im vorherigen Teil bdquoPython-Programmie-rung Teil 2ldquo auf Seite 7 wurden Listen Zei-chenketten und die beiden Kontrollstruk-

turen if und while behandelt Dieses Malwerden mit Funktionen und Modulen zweiwichtige Moumlglichkeiten vorgestellt um eigenePython-Projekte zu strukturieren und wieder-verwendbar zu machen Zunaumlchst sollen abernoch die versprochenen Ersetzungen bei Zei-chenketten besprochen werden Mit den bdquoDic-tionariesldquo wird zudem ein weiterer wichtigerDatentyp in Python vorgestellt

Substitution von ZeichenkettenIn den letzten beiden Teilen dieser Reihe wurdenschon mehrfach einfache Zeichenketten erstelltIn der Regel moumlchte man aber nicht nur bloszligeZeichenketten ausgeben sondern bestimmte dy-namische Informationen darin transportieren et-wa den Namen des Benutzers Dies funktioniertin Python so dass man zunaumlchst Platzhalter inder Zeichenkette definiert und diese spaumlter mitder format()-Methode gegen den gewuumlnschtenInhalt austauscht (substituiert)

gtgtgt message = uHallo 0 du hast 1 Euro yim Portemonnaieformat(uKarl 10)gtgtgt print messageHallo Karl du hast 10 Euro im Portemonnaie

Die Methode format() ersetzt also die Zeichen-folge 0 innerhalb der Zeichenkette durch denersten Parameter die Zeichenfolge 1 durch

den zweiten Parameter usw Python kuumlmmertsich dabei automatisch um das Umwandeln derDatentypen ndash so koumlnnen sehr leicht auch Zahlenin Zeichenketten eingefuumlgt werden ohne dasssich der Benutzer um irgendwelche Umwandlun-gen zu kuumlmmern haumltte Folgendes Beispiel dientzur Veranschaulichung

gtgtgt names = [uKarl uBernd uHannes uIna ]gtgtgt for name in names print u0 hat 1 Buchstabenformat(name len(name))

Hinweis Wie in den Artikeln zuvor steht gtgtgtfuumlr eine Eingabe in der Python-Shell und mussnicht mit eingegeben werden Mit drei Punkten zeigt die Shell an dass ein Befehl noch nichtabgeschlossen ist und sich uumlber mehrere Zeilenerstreckt Diese Punkte muumlssen ebenfalls nichtmit eingeben werden

Die Ausgabe des obigen Beispiels lautet

Karl hat 4 BuchstabenBernd hat 5 BuchstabenHannes hat 6 BuchstabenIna hat 3 Buchstaben

Bisher wurden nur positionale Argu-mente verwendet das heiszligt 0 ver-weist jeweils auf den ersten Parame-ter von format() 1 auf den zwei-

ten etc Um die Uumlbersicht zu wahren ist auch dieAngabe von Namen moumlglich format() erlaubtdabei eine sehr weitreichende Formatierung

gtgtgt for name in names print uusername hat namelength Buchstabenformat( username=name namelength=len(name))

So laumlsst sich etwa festlegen wie viele Nachkom-mastellen ausgegeben werden sollen ob undwie viele Leerzeichen der Zeichenkette vorange-stellt werden sollen und vieles mehr [1]

Achtung In Zeichenketten die mit format() for-matiert werden werden alle geschweiften Klam-mern als Ersetzungszeichen interpretiert Folgen-de Zeile wird also zu einem Fehler fuumlhren

gtgtgt print u0 mag Klammern wie oder format(uBernd)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

KeyError u oder

Hier muumlssen die letzten beiden Klammern mas-kiert werden

gtgtgt print u0 mag Klammern wie yoder format(uBernd)Bernd mag Klammern wie oder

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 14

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Doppelte geschweifte Klammern werden alsovon der format()-Methode ignoriert

DictionariesSogenannte bdquoDictionariesldquo oder bdquoDictsldquo werden inanderen Sprachen oft bdquoHashesldquo oder bdquoassoziati-ve Arraysldquo genannt Wie auch Listen koumlnnen Dic-tionaries beliebige andere Datentypen verwaltenWaumlhrend Listen aber ihre Eintraumlge intern mit fort-laufenden Nummern adressieren (die sogenann-ten Indizes) koumlnnen die Eintraumlge in Dictionariesmit Zeichenketten beliebigen Zahlen oder ande-ren Datentypen adressiert werden Somit bestehtjedes Dictionary aus zwei wesentlichen Elemen-ten Schluumlsseln (keys) und Werten (values)

Ein leeres Dict wird in Python entweder mit derFunktion dict() oder zwei geschweiften Klam-mern erstellt [2]

gtgtgt persons = dict()

und

gtgtgt persons =

sind also aumlquivalent

In folgendem Beispiel sollen nun verschiedenePersonen und ihr jeweiliges Alter in einer Da-tenstruktur gespeichert werden Dies koumlnnte wiefolgt aussehen

gtgtgt persons = uPeter18 uIlse87 uJuergen33 uJutta25

Wie also auch Listen lassen sich Dicts initial be-fuumlllen Die Namen sind in diesem Beispiel je-weils die Schluumlssel das Alter der dazugehoumlrigeWert Schluumlssel und Wert werden durch einenDoppelpunkt getrennt mehrere SchluumlsselWert-Paare durch Kommata

Um das Alter von Peter aus dem Dict auszulesengenuumlgt folgender Aufruf

gtgtgt print persons[uPeter]18

Es faumlllt auf Obwohl Dicts mit geschweiften Klam-mern erstellt werden wird ndash wie auch bei Lis-ten ndash mit eckigen Klammern auf die Werte zuge-griffen Auch sonst gibt es einige Parallelen zwi-schen Dictionaries und Listen Um beispielswei-se zu uumlberpruumlfen ob der Eintrag Hans in einemDict vorhanden ist wird ebenfalls der Operatorin genutzt

gtgtgt if uHans in persons print persons[uHans] else print uDer Eintrag Hans ist nicht vorhanden

Waumlhrend der in-Operator aber bei Listen das Vor-handensein des Wertes Hans abfragt beziehtsich der Operator bei Dicts auf den SchluumlsselHans Ebenso wie bei Listen fuumlhrt der Zugriff aufein nicht vorhandenes ElementSchluumlssel zu ei-nem Fehler Wie man derartige Fehler sehr leichtabfaumlngt wird in einem der folgenden Teile be-sprochen werden

In manchen Situationen ist es aber vielleicht garnicht so wichtig ob ein bestimmter Eintrag nun ineinem Dict vorhanden ist oder nicht Fuumlr solcheFaumllle gibt es die get()-Methode von Dicts

1 gtgtgt print personsget(uHans 15)2 153 gtgtgt print personsget(uPeter 5)4 18

Die Methode get() erwartet als ersten Parame-ter einen beliebigen Schluumlssel Ist der Schluumls-sel im Dict vorhanden wird der dazugehoumlrigeWert zuruumlckgegeben Andernfalls wird der zwei-te Parameter (in Zeile 1 also 15) zuruumlckgegebenSo lassen sich beispielsweise Standardwerte fuumlrnicht vorhandene Schluumlssel implementieren

Gut zu sehen ist dass der Aufruf in Zeile 3 nicht5 sondern 18 zuruumlck gibt denn dieser Wert wur-de oben dem Schluumlssel Peter zugewiesen

Der zweite Parameter der Methode get() ist op-tional Er muss nicht angegeben werden Wirdkein zweiter Parameter angegeben gibt die Me-thode None zuruumlck wenn der gesuchte Schluumlsselim Dict nicht vorhanden ist

gtgtgt print personsget(uAnke)None

Natuumlrlich koumlnnen auch jederzeit weitere Eintraumlgezu Dicts hinzugefuumlgt oder bestehende Eintraumlgeveraumlndert werden

1 gtgtgt persons[uPeter] = 992 gtgtgt print persons[uPeter]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 15

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

3 994 gtgtgt persons[uHans] = 155 gtgtgt print persons[uHans]6 15

In Zeile 1 wird das Alter von Peter auf 99 veraumln-dert In Zeile 4 wird der Eintrag Hans hinzugefuumlgt

Dictionaries lassen sich ndash ebenso wie Listen ndashauch sehr leicht verschachteln In folgendem Bei-spiel wird ein verschachteltes Dict genutzt umein kleines Adressbuch zu implementieren

1 gtgtgt addresses = 2 uPeterustreetMusterstr 16 umobile0151123 4563 uJuttaustreetBeispielstr 99 umobile015133 44 554

Hier wird ndash zur besseren Lesbarkeit uumlber meh-rere Zeilen verteilt ndash ein verschachteltes Dict er-stellt In Zeile 1 wird dem Namen addresses einDict zugewiesen Dieses wird dabei direkt initialbefuumlllt Den Schluumlsseln Peter und Jutta wird da-bei nicht wie oben ihr Alter als Wert zugeteilt son-dern es dienen Dicts als Werte

Der Aufruf

gtgtgt print addresses[uPeter]

gibt nun das dazugehoumlrige Dict zuruumlck

umobile 0151123 456 ustreet Musterstr 16

Auf dieses Dict kann auch direkt zugegriffen wer-den

gtgtgt print addresses[uPeter][ustreet]Musterstr 16

Dieser Aufruf irritiert vielleicht zunaumlchst Etwasverstaumlndlicher (aber laumlnger) ist folgende Vorge-hensweise

1 gtgtgt peters_data = addresses[uPeter]2 gtgtgt type(peters_data)3 lttype dictgt4 gtgtgt peters_data[ustreet]5 Musterstr 16

In Zeile 1 wird der zum Schluumlssel Peter gehoumlri-ge Wert der Variable peters_data zugewiesen

Dieser Wert ist wiederum ein Dict mit den zuPeter gehoumlrigen Adressdaten (siehe obiges Bei-spiel) Zeile 2 und 3 zeigen dass die Variablepeters_data auf ein Dict zeigt In Zeile 4 und 5wird nun wiederum der Schluumlssel street dieseszweiten Dicts ausgegeben

Aus dem Ausdruck

gtgtgt print addresses[uPeter][uystreet]

wird also zunaumlchst

ustreetuMusterstr 16 uymobile0151123 456[ustreet]

was schlieszliglich auf den Wert Musterstr16 verweist

Insgesamt eignen sich Dicts hervorragendum Datenstrukturen wie Woumlrterbuumlcher oderAdressbuumlcher abzubilden Uumlberall dort wo

Informationen uumlber bestimmte Schluumlssel zu-gaumlnglich sein sollen empfiehlt sich die Ver-wendung von Dictionaries

Ebenso wie uumlber Listen kann sehr leicht uumlberDicts iteriert werden Dabei ist aber zu beach-ten dass Dicts keine feste Reihenfolge ken-

nen Es gibt also keine Garantie dafuumlr dass dieSchluumlssel eines Dicts beim Iterieren in der Rei-henfolge durchlaufen werden in der sie erstelltwurden [3]

FunktionenFunktionen sind ein wichtiges Strukturierungs-merkmal moderner ProgrammiersprachenDurch sie koumlnnen haumlufig benoumltigte Arbeitsschrit-te leicht wiederverwertet werden Damit dienensie auch der Lesbarkeit und Wartbarkeit desQuelltextes

Eine Funktion wird in Python wie folgt deklariert

def say_hallo()print uHallo

Diese Funktion kann nun einfach mitsay_hallo() aufgerufen werden und wird beijedem Aufruf die Meldung Hallo auf dem Bild-schirm ausgeben Das Schluumlsselwort def leitethierbei die Deklaration einer Funktion ein da-nach folgt der Name der Funktion der nach demPaar runden Klammern mit einem Doppelpunktabgeschlossen wird Zu beachten ist dass der

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 16

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Rumpf von Funktionen einzuruumlcken ist ndash wie beiallen Kontrollstrukturen in Python

Natuumlrlich handelt es sich bei dem obigen Beispielnoch um eine sehr einfache Funktion FolgendeFunktion greift ein Beispiel aus Teil 1 dieser Rei-he wieder auf und wird beliebige Zeichenkettenmit einer Box aus Leerzeichen umgeben

1 def boxify(text)2 text_with_borders = u= 0 =format(text)3 line = len(text_with_borders) u=45 output = u0n1n2format(line text_with_borders line)6 return output

In Zeile 1 wird ndash wie gehabt ndash eine Funktion de-finiert Sie hat den namen boxify und erwartetgenau einen Parameter (hier text) Es koumlnnenprinzipiell natuumlrlich beliebig viele Parameter imFunktionskopf definiert werden ndash getrennt wer-den sie durch Kommata

In Zeile 2 wird links und rechts der uumlbergebenenZeichenkette text ein Gleichheits- und Leerzei-chen eingefuumlgt in Zeile 3 wird eine Zeichenket-te erstellt die ausschlieszliglich Gleichheitszeichenenthaumllt

Zeile 5 wirkt nur auf den ersten Blick kompli-ziert Wie bereits in Teil 2 dieser Reihe eroumlr-tert wurde handelt es sich bei der Zeichenfol-ge n um eine sogenannte Escape-Sequenz dieeinen Zeilenumbruch erzeugt Somit beinhaltetdie Zeichenkette output drei Zeilen Die ersteZeile enthaumllt ausschlieszliglich Gleichheitszeichen

die zweite Zeile die uumlbergebene Zeichenkette mitGleichheitszeichen links und rechts die dritte Zei-le schlieszliglich erneut nur Gleichheitszeichen

Neu ist das Schluumlsselwort return ndash es gibt dennachfolgenden Ausdruck (hier output) zuruumlck

Wird diese Funktion nun aufgerufen ge-schieht zunaumlchst scheinbar nichts Die Funktion

boxify()macht keine Bildschirmausgaben son-dern gibt nur eine Zeichenkette zuruumlck Diesemuss also noch an die print()-Funktion weiter-gegeben werden

gtgtgt print boxify(uMein Haus mein yGarten meine Box)====================================== Mein Haus mein Garten meine Box ======================================

Eine Besonderheit ist bei return noch zu beach-ten Die Anweisung bricht die Funktion sofort abund liefert einen Ruumlckgabewert ndash nachfolgendeCodezeilen der Funktion werden nicht mehr aus-gefuumlhrt

1 def test()2 print uHallo3 return4 print uDies ist ein Test

56 print ustart7 test()8 print uende

Dieses Beispiel gibt nur die folgende Meldungaus

startHalloende

Die Zeile 4 (Dies ist ein Test) kommt nie-mals zur Ausfuumlhrung Gut zu sehen ist auchin welcher Reihenfolge die Anweisungen ausge-fuumlhrt werden Zwar wird in den Zeilen 1 bis 4 dieFunktion definiert ndash sie wird aber noch nicht aus-gefuumlhrt Es muss also immer zwischen der bdquoDe-klarationldquo einer Funktion und dem bdquoAufrufldquo dersel-ben unterschieden werden Zuerst wird hier dem-nach die Meldung start ausgegeben Dann wirddie Funktion aufgerufen und abgearbeitet ndash dieMeldung Hallo erscheint Durch die Anweisungreturn wird der Programmfluss nun in Zeile 8fortgesetzt ndash ende wird ausgegeben

Zu beachten ist dass Funktionen keine return-Anweisung haben muumlssen ndash haben sie keinekehrt der Programmfluss nach dem Abarbeitendes Funktionsrumpfes zum Ausgangspunkt zu-ruumlck Der Ruumlckgabewert der Funktion ist dannNone

Eine weiteres wichtiges Element von Funktionensind Standardparameter Sie werden durch einGleichheitszeichen definiert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 17

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

def say_something(what who=uKarl)print u0 sagt 1format(who what)

say_something(uHi)say_something(uTach auch uBernd)

Der Parameter what ist obligatorisch ndash bei je-dem Funktionsaufruf muss er angegeben wer-den sonst kommt es zu einem Fehler Der zwei-te Parameter hingegen ist optional Wird er nichtangegeben erhaumllt er automatisch den Wert KarlEntsprechend gibt obiges Skript folgende Ausga-be aus

Karl sagt HiBernd sagt Tach auch

Wichtig ist Bei der Funktionsdefinition muumlssenzuerst immer die obligatorischen Parameter an-gegeben werden Die Funktion

def say_something(who=Karl what)

fuumlhrt also zu einem Fehler beim Versuch dasSkript auszufuumlhren

Parameter an Funktionen uumlbergebenIn den obigen Beispielen wurden bereits ver-schiedene Parameter an die Funktionen uumlberge-ben Etwa Tach auch und Bernd an die Funk-tion say_something() In dem Beispiel musssich der Programmierer peinlich genau an die imFunktionskopf definierte Reihenfolge halten DieParameter sind also abhaumlngig von der Position ndashsie sind bdquopositionalldquo

Als Beispiel sei die folgende(nicht besonders schoumlne) Funk-tion gegeben bei der die Argu-mente alle in einer bestimmtenReihenfolge angegeben werdenmuumlssen

def print_a_lot(name country street adress mobile age sex hobbies)print uname stammt aus country format(name=name country=country)

Statt hier nun die erforderlichen Argumente im-mer in der einzig richtigen Reihenfolge anzuge-ben gibt es noch die Moumlglichkeit die Argumenteper Schluumlsselwort zu uumlbergeben

print_a_lot(name=uBernd age=18 ysex=um street=uMusterstrasse yadress=18 country=uDeutschland ymobile=u0151-123456789 hobbies=uylesen)

Diese Art des Aufrufes kann die Lesbarkeit vonQuelltexten enorm erhoumlhen

def print_info(name country=None street=None adress=None mobile=None yage=None sex=None hobbies=None)

if ageprint uHallo 0 Du bist 1 Jahre altformat(name age)

elseprint uHallo 0format(name)

Die Funktion print_info() unterscheidet sichvon print_a_lot() darin dass alle Parameterbis auf name optional sind ndash sie muumlssen nicht an-gegeben werden Wird aber ein Alter uumlbergeben(age) wird zusaumltzlich zum Namen auch das Alterausgegeben Der Aufruf ist

print_info(uJutta age=25)

Der erste Parameter ist positional Hier wirdder Name uumlbergeben Da alle anderen Parame-ter optional sind werden sie einfach ausgelas-sen Lediglich der age-Parameter wird noch als

Schluumlsselwort-Argument uumlbergeben

In Python sind auch Funktionen ganz normaleObjekte Auch sie lassen sich damit beispielswei-se an Namen binden

1 gtgtgt from operator import add2 gtgtgt add(1 3)3 44 gtgtgt plus = add5 gtgtgt plus6 ltbuilt-in function addgt7 gtgtgt plus(1 3)8 4

Achtung Wichtig ist hier dass die Funktionin Zeile 5 ohne runde Klammern an einen Na-men gebunden wird Andernfalls wuumlrde die Funk-tion aufgerufen und das Ergebnis an den Na-men gebunden werden Anders gesagt Mit Klam-mern wird eine Funktion aufgerufen ohne Klam-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 18

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

mern wird das Funktionsobjekt angesprochendie Funktion aber nicht ausgefuumlhrt

In Zeile 1 wird zunaumlchst die Funktion add()aus dem Modul operator importiert (siehe dazunaumlchster Abschnitt) Die Funktion add() addiertndash wie der Operator + ndash zwei Zahlen Sie stellt diegleiche Funktionalitaumlt nur als Funktion zur Verfuuml-gung (Zeile 3)

In Zeile 5 wird die Funktion add() zunaumlchst anden Namen plus gebunden In Zeile 6 und 7wird gezeigt dass der Name plus noch immerauf die Funktion add() verweist ndash add und plussind identisch

In Zeile 9 wird deutlich dass man an Namen ge-bundene Funktionen genau so wie normale Funk-tionen aufrufen kann Am Ende dieses Teils wirddiese Technik an einem praktischen Beispiel ver-anschaulicht

ModuleModule bieten eine einfache Moumlglichkeit seineSkripte um zusaumltzliche Funktionen zu erweiternEs wurde bereits angesprochen dass Python miteiner Vielzahl zusaumltzlicher Bibliotheken ausgelie-fert wird ndash diese Bibliotheken heiszligen in PythonModule Sie werden durch den Befehl import inein eigenes Skript eingebunden [4]

1 import os23 counter = 045 files = oslistdir()

6 for entry in files7 if ospathisfile(entry)8 counter += 1910 print uEs gibt 0 Dateien in ydiesem Verzeichnisformat(ycounter)

In Zeile 1 wird der Interpreter angewiesen dasModul os im jetzigen Skript verfuumlgbar zu machenDieses Modul enthaumllt verschiedene Funktionenzum Kopieren Verschieben oder Loumlschen vonDateien Auch das Auflisten des aktuellen Ver-zeichnisinhaltes gehoumlrt dazu [5]

In Zeile 5 wird die Funktion listdir() des Mo-dules os aufgerufen Der Parameter verweistauf das aktuelle Verzeichnis Die Funktion erstellteine Liste mit allen Dateien und Ordnern des ak-tuellen Verzeichnisses und gibt diese Liste zu-ruumlck Die Variable files zeigt nun auf diese Lis-te

In Zeile 6 wird eine for-Schleife definiert dieuumlber die zuvor erstellte Liste iteriert In Zeile 7wird uumlberpruumlft ob es sich bei dem jeweiligen Ein-trag um eine Datei oder ein Verzeichnis handelt ndashauch dazu wird eine Funktion aus dem Modul osgenutzt Falls es sich um eine Datei handelt gibtdiese Funktion True zuruumlck andernfalls FalseNur im ersten Fall ist die if-Bedingung wahr undder Zaumlhler wird erhoumlht

Nach dem Durchlauf der Schleife setzt der nor-male Programmfluss fort ndash die letzte Zeile des

Skriptes wird ausgefuumlhrt und zeigt die Zahl derDateien im aktuellen Verzeichnis an

Es gibt auch eine Moumlglichkeit Funktionen ausModulen so zu importieren dass sie im Skriptdirekt verfuumlgbar sind ndash mit der Anweisung fromMODUL import FUNKTIONOBJEKT So koumlnntees im obigen Beispiel etwa heiszligen

from os import listdir

Allerdings muumlsste dann auch Zeile 5 ange-passt werden Da durch den from-Import dielistdir()-Funktion direkt im Namensraum [5]des Skriptes verfuumlgbar ist muumlsste die Zeile nunwie folgt lauten

files = listdir()

Das sieht auf den ersten Blick sehr bequem ausfuumlhrt aber jetzt in Zeile 7 zu Problemen Da nurdie Funktion listdir importiert wurde ist ein Zu-griff auf die Funktion ospathisfile nicht moumlg-lich Diese Funktion muumlsste nun zusaumltzlich impor-tiert werden

Das Importieren von einzelnen Funktionen hatnoch andere Nachteile [6] So erschwert esdie Verstaumlndlichkeit des Quelltextes da Frem-de nicht wissen ob mit listdir hier die Funk-tion aus dem os-Modul gemeint ist oder viel-leicht irgendwo im Quelltext noch eine eigenelistdir-Funktion zu finden ist die etwas ganzanderes macht In aller Regel sollte daher vonfrom-Importen abgesehen und die zusaumltzlicheSchreibarbeit in Kauf genommen werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 19

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Was es nicht alles gibtDas os-Modul erscheint noch recht bodenstaumln-dig Bisher wurde damit der Inhalt eines Ver-zeichnisses aufgelistet und uumlberpruumlft ob eine be-stimmte Datei ein Ordner oder eine Datei ist Dar-uumlber hinaus gibt es aber Module fuumlr grafischeOberflaumlchen [7] fuumlr Datenbanken [8] fuumlr die Bild-bearbeitung [9] oder fuumlr mathematische und wis-senschaftliche Anforderungen [10] Es gibt Mo-dule um Spiele zu programmieren [11] Moduleum automatisiert Eingaben in Webseiten vorzu-nehmen [12] Module fuumlr Mediendateien [13] fuumlrIMAP [14] oder sogar BitTorrent [15] Nicht allediese Module gehoumlren dabei zum Standardum-fang [16] der Sprache ndash die fehlenden lassen sichaber leicht uumlber die Paketquellen oder das eigensfuumlr Python entwickelte EasyInstall-System instal-lieren

Module selbst gemachtEs stellt sich nun natuumlrlich die Frage wie sich der-artige Module selbst erstellen lassen Die meis-ten Leser dieser Reihe werden dabei vermutlichschon lange das eine oder andere Python-Modulerstellt haben

1 usrbinenv python2 -- coding utf-8 --34 def boxify(text)5 text_with_borders = u= 0 =format(text)6 line = len(text_with_borders) u=78 output = u0n1n2format(line text_with_borders line)9 return output

Listing 1 boxpy

Ein Python-Modul ist zunaumlchst naumlmlich nichts an-deres als eine Textdatei mit der Endung py Dieoben besprochene Funktion boxify() soll imFolgenden in ein eigenes Modul ausgegliedertwerden

Diese obigen Zeilen werden in die Datei boxpygespeichert Die ersten beiden Zeilen wurden be-reits im ersten Teil dieser Reihe besprochen Eshandelt sich um die Shebang-Zeile und um dieAngabe der Zeichenkodierung Auch die Funk-tion boxify() sollte mittlerweile hinlaumlnglich be-kannt sein

Damit wurde nun das Modul box erstellt Der Mo-dulname entspricht also immer dem Dateinamenabzuumlglich der Endung py

1 usrbinenv python2 -- coding utf-8 --34 import box56 name = unicode(raw_input(uBitte yNamen eingeben ))

7 print boxboxify(name)

Listing 2 myprogpy

Um dieses Modul nun verwenden zu koumlnnenwird eine weitere Python-Datei im selben Ver-zeichnis erstellt myprogpy (siehe oben)

In Zeile 4 wird das selbst erstellte Box-Modul im-portiert In Zeile 6 wird der Benutzer zur Eingabeseines Namens aufgefordert In Zeile 7 schlieszlig-lich wird die Funktion boxify() aus dem box-Modul mit dem eingegebenen Namen aufgeru-fen Das Resultat wird mit print auf dem Bild-schirm ausgegeben

Einschraumlnkungen und ErweiterungenDer Python-Interpreter hat eine recht genaueVorstellung davon in welchen Verzeichnissensich ein Modul befinden darf [17] Befindet sichdas Modul nicht in einem dieser Verzeichnis-se kommt es zu einem Fehler Der Python-Interpreter schaut aber zusaumltzlich auch immer indas Programmverzeichnis ndash hier also etwa in dasVerzeichnis der Datei myprogpy So lange dieselbst erstellten Module in diesem Verzeichnis zufinden sind befindet sich der Anfaumlnger also aufder sicheren Seite

Eine Erweiterung des Modul-Systems stellen diesogenannten bdquoPackagesldquo dar [18] Damit lassensich verschiedene zusammengehoumlrige Modulebuumlndeln und strukturieren In dieser Einfuumlhrungwird aber auf eine weitergehende Behandlungdieser Thematik verzichtet Wer aber groumlszligere Bi-bliotheken programmieren moumlchte oder eine gan-ze bdquoWerkzeugsammlungldquo in Modulen organisie-ren will sollte sich Packages einmal naumlher anse-hen [19]

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 20

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Nachdem nun Listen Dictionaries Zeichenket-ten Module Funktionen und verschiedene Kon-trollstrukturen in den ersten drei Teilen dieser Ein-fuumlhrung besprochen wurden sollen im naumlchstenTeil Klassen vorgestellt werden

Praktisches Beispiel Der Taschen-rechner

In folgendem Beispiel kommen verschiedene be-reits erlernte Techniken zum Einsatz

1 usrbinenv python2 -- coding utf-8 --34 from operator import add sub ymul div

56 def info(args)7 print Moegliche Befehle8 print join(dispatch)9 print Syntax BEFEHL [y

PARAM_A PARAM_B]1011 dispatch = 12 uhelp info13 uadd add14 usub sub15 umul mul16 udiv div17 uaddiere add18 uplus add19 2021 def parser()22 while True

23 user_command = unicode(yraw_input(calc ))

24 tokens = user_commandysplit()

25 command = tokens[0]26 arg_a arg_b = None None27 if len(tokens) gt 128 arg_a = int(tokensy

[1])29 arg_b = int(tokensy

[2])30 print tokens31 if command == uquit32 return33 elif command in dispatch34 result = dispatch[y

command](arg_a arg_by)

35 print gtgtgt 0yformat(result)

36 else37 print Unbekanntes y

Kommando 0yformat(command)

38 print Tippe help yfuer Hilfe

3940 if __name__ == __main__41 parser()

Listing 3 calcpy

In Zeile 4 werden die Funktionen add sub mulund div aus dem Modul operator importiertSie stellen die Funktionalitaumlt der Operatoren + - und als Funktion zur Verfuumlgung (siehe oben)

In Zeile 6 wird die Funktion info() definiert DasSternchen () vor dem Parameter args fuumlhrt da-zu dass die Funktion beliebig viele (positionale)Argumente akzeptiert und diese als Liste argsbereitstellt Keine Sorge Dieses Vorgehen dientin diesem Fall nur dazu den Taschenrechner un-empfindlicher gegen Fehleingaben zu machenDie Funktion ignoriert alle Parameter und gibt le-diglich alle moumlglichen Befehle durch Kommatagetrennt aus (Zeile 8)

In Zeile 11 wird ein Dict definiert und initial befuumllltDen Schluumlsseln werden hier Funktionen als Wer-te zugewiesen Oder anders Die Funktionen wer-den an Schluumlssel des Dicts gebunden Ein Aufrufvon dispatch[plus](4+1) ist im Folgendenalso identisch mit einem Aufruf von add(4 1)In Zeile 13 17 und 18 ist zu sehen dass dieFunktion add gleich mehreren Dict-Schluumlsselnzugewiesen wird Jeder Dict-Schluumlssel ist dabeiein moumlglicher Befehl Der Benutzer wird spaumlteralso mit addiere add und plus gleichermaszligenaddieren koumlnnen

Das Herzstuumlck des Taschenrechners ist die Funk-tion parse() die in Zeile 21 definiert wird In Zei-le 22 wird weiterhin eine Schleife implementiertDa die Bedingung True immer wahr ist handeltes sich hierbei erst einmal (scheinbar) um eineEndlosschleife

In Zeile 23 wird eine Benutzereingabe eingele-sen Diese koumlnnte etwa wie folgt aussehen

add 3 7

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 21

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

In Zeile 24 wird diese Benutzereingabe durch dieFunktion split() aufgeteilt Da diese Funktionhier ohne Parameter aufgerufen wird teilt sie dieZeichenkette bei allen Leerraumlumen (Whitespace-Zeichen) Die Eingabe von add 3 7 wuumlrde so zurListe [add 3 7] fuumlhren die dem Na-men tokens zugewiesen wird

In Zeile 25 wird das erste Element der Liste (derBefehl) dem Namen command zugewiesen Wei-terhin werden zwei Variablen arg_a und arg_berstellt Sie sollen spaumlter die Zahlen enthaltendie addiert werden sollen zeigen aber zunaumlchstnur auf None

Zeile 27 testet ob die Liste tokens mehr alsnur ein Element enthaumllt Ist dies der Fall werdendas zweite und dritte Element der Eingabe (al-so 3 und 7 wenn man vom obigen Beispiel aus-geht) mit der int()-Funktion in Zahl-Objekte um-gewandelt und den Namen arg_a bzw arg_b zu-gewiesen In Zeile 30 wird die Liste tokens aus-gegeben

In Zeile 30 ff findet sich nun eine if-Kontrollstruktur Nach der Eingabe von quit solldas Programm beendet werden Dazu wird mitdem Schluumlsselwort return die Abarbeitung derFunktion parse direkt unterbrochen Die in Zeile22 definierte bdquoEndlosschleifeldquo verfuumlgt damit alsodoch uumlber eine Abbruchbedingung

Das Schluumlsselwort elif in Zeile 33 wurde auchbereits besprochen Es wird getestet ob die ers-te Benutzereingabe (add im vorherigen Beispiel)

im Dict dispatch (Zeile 11) vorhanden ist In Zei-le 34 geschieht nun die eigentlich bdquoMagieldquo des Ta-schenrechners

result = dispatch[command](arg_a yarg_b)

Abhaumlngig vom Inhalt der Variable command wirdnun der dazugehoumlrige Schluumlssel im Dict ange-sprochen Da diesen Schluumlsseln aber in Zei-le 11 ff Funktionen zugewiesen wurden wirdmit der Anweisung dispatch[command] abhaumln-ging von command eine Funktion angesprochenDie Klammern hinter dieser Anweisung (arg_aarg_b) enthalten also die Parameter fuumlr dieseFunktion Das Ergebnis der jeweiligen Funktionist nun uumlber die Variable result verfuumlgbar

Was passiert hier also Der Taschenrechner liestBenutzereingaben in der Form BEFEHL ZAHL1ZAHL2 ein Ist nun BEFEHL ein Schluumlssel im Dictdispatch wird die dazugehoumlrige Funktion auf-gerufen Der Befehl addiere wuumlrde somit zumAufruf der Funktion add fuumlhren der Befehl infozum Aufruf der Funktion info Die EingabenZAHL1 und ZAHL2 werden dabei jeweils als Pa-rameter uumlbergeben Dies ist auch der Grundwarum die in Zeile 6 definierte Funktion info mitargs beliebig viele Parameter uumlbernimmt Soreagierte sie tolerant auf eventuelle Falschanga-ben des Benutzers denn diese werden in jedemFall an die Funktion uumlbergeben

In Zeile 35 wird das Ergebnis der aufgerufenenFunktionen formatiert und ausgegeben Da in der

Funktion info kein Ruumlckgabewert festgelegt wur-de gibt sie immer None zuruumlck

In Zeile 36-38 wird nun schlieszliglich fuumlr den Fallvorgesorgt dass BEFEHL nicht im Dict dispatchvorkommt Dann hat der Benutzer eine unguumlltigeEingabe gemacht und wird darauf hingewiesen

Der if-Block in Zeile 40 f stellt schlieszliglich sicherdass die Funktion parser() nur ausgefuumlhrt wirdwenn das Python-Skript direkt gestartet wurdeWuumlrde man das Skript als Modul importieren (sie-he oben) wuumlrde der Taschenrechner nicht auto-matisch ausgefuumlhrt werden

Natuumlrlich wirkt dieser Taschenrechner auf denersten Blick sehr kompliziert Er zeigt aberwarum das Binden von Funktionen an Namen(oder hier Dict-Schluumlssel) sehr sinnvoll seinkann Ohne diese Technik muumlsste der Program-mierer jede Eingabe von Hand auswerten

if command == add or command == yaddiere or command == plus

result = arg_a + arg_belif command == sub

result = arg_a - arg_belif command == div

result = arg_a arg_belif command == mul

result = arg_a arg_belif command == info

info()elif command == quit

returnelse

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 22

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

print Unbekanntes Kommando y0format(command)print Tippe help fuer Hilfey

print gtgtgt 0format(result)

LINKS

[1] httpdocspythonorglibrarystringhtmlformat-string-syntax

[2] httpdocspythonorgtutorialdatastructureshtmldictionaries

[3] httpwwwpythonorgdevpepspep-0372[4] httpdocspythonorgtutorialmoduleshtml[5] httpdocspythonorglibraryoshtml[6] httpeffbotorgzoneimport-confusionhtm

[7] httpdewikipediaorgwikiListe_von_GUI-BibliothekenPython

[8] httpinitdorgtrackerpysqlite[9] httpwwwpythonwarecomproductspil

[10] httpwwwscipyorg[11] httpwwwpygameorgnewshtml[12] httpwwwsearchsourceforgenetmechanize[13] httppymediaorg[14] httpdocspythonorglibraryimaplibhtml[15] httpwwwrasterbarcomproductslibtorrent

python_bindinghtml[16] httpdocspythonorgmodindexhtml[17] httpdocspythonorgusingcmdlinehtmlenvvar-

PYTHONPATH[18] httpdocspythonorgtutorialmoduleshtml

packages

[19] httpdocspythonorgdistutilsindexhtml

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLithium Batteriesldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom560

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 23

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Python-Programmierung Teil 4 ndash Klassenunterschiede von Daniel Noumlgel

Im dritten Teil dieser Einfuumlhrung bdquoPython-Programmierung Teil 3ldquo auf Seite 14 wur-den mit Funktionen und Modulen zwei ele-

mentare Konstrukte zur Strukturierung vonPython-Skripten vorgestellt In diesem Teilsoll nun mit Klassen ein weiteres wichtigesKonstrukt besprochen werden

Objektorientierte ProgrammierungBei der objektorientierten Programmierung(OOP [1]) handelt es sich um ein sogenanntesProgrammierparadigma ndash also um ein bestimm-tes Entwurfsmuster beim Programmieren Beider OOP wird versucht Daten und Funktionenzu sinnvollen Einheiten zusammenzufassen undan sogenannte Objekte zu binden

Neben bdquoObjektenrdquo gibt es zumindest noch dreiweitere wichtige Begriffe im Zusammenhang mitobjektorientierter Programmierung die im Fol-genden immer wieder auftauchen bdquoKlassenldquo bdquoAt-tributeldquo und bdquoMethodenldquo

Als Beispiel sollen fuumlr eine Rennsimulation unter-schiedliche Autos zur Verfuumlgung stehen DieseAutos unterscheiden sich in Laumlnge Gewicht Ge-schwindigkeit Beschleunigung Farbe etc Wennnun 20 unterschiedliche Autos gleichzeitig um dieWette fahren muumlssen viele Daten verwaltet wer-den Es bietet sich an diese Daten jeweils zu-sammenzufassen und zu buumlndeln So koumlnnte esbeispielsweise eine Objekt bdquoAutoldquo mit folgendenEigenschaften geben

Laumlnge Gewicht Geschwindigkeit Beschleunigung Farbe

Diese Definition der spaumlteren Auto-Objekte imQuelltext eines Programmes nennt man KlasseJedes Objekt wird aus dieser Klasse erstellt ndashman spricht auch von der Instanz einer KlasseKlasse und Objekt verhalten sich also stark ver-einfacht ausgedruumlckt so zueinander wie bdquoBau-planldquo und bdquoAuto auf der Straszligeldquo

1 class Car(object)2 def __init__(self speed=0 max_speed=140 acceleration=5 color=y

grey)3 selfspeed = speed4 selfmax_speed = max_speed5 selfacceleration = acceleration6 selfcolor = color78 def brake(self)9 selfspeed -= selfacceleration10 print Current Speed 0format(selfspeed)1112 def accelerate(self)13 selfspeed += selfacceleration14 print Current Speed 0format(selfspeed)1516 def honk(self)17 print Tuuuuut Tuuuut

Listing 1 BeispielKlasseCarpy

Bei Laumlnge Gewicht und den anderen Anga-ben handelt es sich offensichtlich um Eigen-schaften der jeweiligen Autos Eigenschaftenvon Objekten nennt man Attribute Objekte koumln-nen aber auch Funktionen besitzen ndash also et-wa bremse_ab() oder gib_gas() Diese Funk-tionen von Klassen bzw Objekten nennt man Me-thoden

Klassen in PythonDas Beispiel BeispielKlasseCarpy zeigt wieman eine (leicht abgewandelte) Auto-Klasse inPython umsetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 24

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

In Zeile 1 wird die Klasse definiert Dies wirddurch das Schluumlsselwort class gekennzeichnetIn Klammern dahinter wird angegeben von wel-chen anderen Klassen die neue Klasse erbensoll (siehe dazu den naumlchsten Abschnitt) In aumll-teren Python-Versionen sollte hier immer vonobject geerbt werden ndash nur dann steht der volleFunktionsumfang der Klassen in Python zu Ver-fuumlgung Wer bereits Python gt= 3 verwendet ver-zichtet auf diese Angabe und hat dennoch denvollen Funktionsumfang der Klassen Wie jedeKontrollstruktur in Python wird auch der Kopf derKlasse mit einem Doppelpunkt abgeschlossen

In Zeile 2 wird die Spezialmethode __init__ de-finiert Diese wird aufgerufen wenn eine Instanzder Klasse (also ein Objekt) erstellt wird Die Me-thode __init__ wird hier mit fuumlnf Parametern de-finiert self speed max_speed accelerationund color

In Python muss jede Methode (egal ob Spezial-methoden wie __init__() oder eigene Metho-den wie brake()) als erstes den Parameter selfakzeptieren Durch diesen Parameter kann spauml-ter zu jeder Zeit auf die Attribute und Metho-den eines Objektes zugegriffen werden selfist also ndash wie der Name schon andeutet ndash ei-ne (Selbst)Referenz des spaumlteren Objektes Dasmag zunaumlchst umstaumlndlich und laumlstig wirken ndashtatsaumlchlich gibt es aber gute Gruumlnde fuumlr diesesVorgehen [2]

Die uumlbrigen Parameter (speed color) sindoptional Sie definieren die Standardwerte fuumlrdas Auto-Objekt

In den Zeilen 3 bis 6 werden die Parameter anKlassenattribute gebunden Erst dadurch habendie spaumlteren Objekte auch tatsaumlchlich die ent-sprechenden Eigenschaften

In Zeile 8 wird eine Methode brake() definiertAls Klassenmethode muss auch sie den Para-meter self akzeptieren In Zeile 9 wird deutlichwarum Uumlber diesen Parameter kann wieder aufdas in Zeile 3 definierte Attribut speed zugegrif-fen werden Im Fall der Methode brake() wirddas Attribut speed um die Houmlhe des Attributsacceleration reduziert

In den Zeilen 12 und 16 werden weiterhin nochdie Methoden accelerate() und honk() defi-niert

Klassen zum Leben erweckenDie Klasse Car wurde definiert Bisher gibt esaber noch keine Instanz dieser Klasse Im Fol-gendem werden zwei Car-Objekte erstellt

1 gtgtgt car1 = Car()2 gtgtgt car2 = Car(speed=50)3 gtgtgt car1speed4 05 gtgtgt car2speed6 507 gtgtgt car1accelerate()8 Current Speed 59 gtgtgt car1accelerate()10 Current Speed 1011 gtgtgt car2brake()12 Current Speed 4513 gtgtgt car1honk()14 Tuuuuut Tuuuut

In den Zeilen 1 und 2 werden Instanzen der Klas-se Car erstellt Dabei wird implizit die Funktion__init__() aufgerufen Fuumlr car1 werden kei-ne Parameter uumlbergeben sodass die Standard-Parameter greifen (speed=0) Fuumlr car2 wird da-hingegen speed=50 uumlbergeben so dass dasspeed-Attribut dieser Car-Instanz von Anfang anauf 50 gesetzt wird

Die Zeilen 3 und 5 veranschaulichen dies Hierwird jeweils das speed-Attribut der beiden Car-Objekte ausgelesen Fuumlr car1 ist es 0 fuumlr car2erwartungsgemaumlszlig 50 In den Zeilen 7 und 9 wirdcar1 jeweils beschleunigt Da die Beschleuni-gung acceleration in Zeile 2 der Klassendefi-nition mit 5 angegeben wurde wird das speed-Attribut jeweils um 5 erhoumlht

In Zeile 11 wird die brake() Methode von car2aufgerufen Das speed-Attribut vermindert sichdamit von 50 auf 45

Zeile 13 zeigt dass Klassenmethoden nicht im-mer Attribute der Klasse manipulieren muumlssenHier bdquohuptldquo car1 indem die aufgerufenen Metho-de eine entsprechende Meldung auf dem Bild-schirm ausgibt

Das Beispiel zeigt wie aus dem Bauplan Carzwei Car-Objekte erstellt und an die Namen car1bzw car2 gebunden wurden Diese Objekte sindvoumlllig unabhaumlngig voneinander Veraumlnderungenam Attribut speed von car1 wirken sich nichtauf car2 aus und umgekehrt Theoretisch koumlnn-ten nun beliebig viele weitere Car-Objekte erstelltwerden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 25

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

gtgtgt ferrari = Car(max_speed=280 acceleration=10 color=red)gtgtgt trabbi = Car(max_speed=70 acceleration=2 color=blue)

VererbungVererbung ist ein weiteres maumlchtiges Elementder OOP Es ist damit moumlglich Attribute und Me-thoden von anderen Klassen zu bdquoerbenldquo und wei-tere Attribute und Methoden hinzuzufuumlgen Sokoumlnnen bequem Klassen konstruiert werden dieaufeinander aufbauen und sich gegenseitig er-weitern

Oben wurde bereits die Klasse Car fuumlr eine fik-tive Rennsimulation definiert Durch Vererbunglassen sich spezifischere Klassen von Autos er-stellen ndash etwa eine Rennwagenklasse mit hoherBeschleunigung und hoher Endgeschwindigkeit

1 class Racer(Car)2 def __init__(self color=red)3 Car__init__(self speed=0 max_speed=400 acceleration=10 colory

=color)4 selfcurrent_gear = 056 def honk(self)7 print Tuuuuut Tuuuut Tuuuut89 def shift_up(self)10 selfcurrent_gear += 111 print Current gear 0format(selfcurrent_gear)1213 def shift_down(self)14 selfcurrent_gear -= 115 print Current gear 0format(selfcurrent_gear)

Listing 2 BeispielKlasseRacerpy

Die Definition von BeispielKlasseRacerpyaumlhnelt sehr der bei der Klasse Car Allerdingswird hier von Car geerbt (Zeile 1) Die neue Klas-se Racer hat also zunaumlchst die gleichen Attribu-te und Methoden wie die Klasse Car Die Init-Methode unterschiedet sich aber schon deutlichSie kennt neben dem Parameter self nur nochden Parameter color

Nun wurde zwar von der Klasse Car geerbtndash diese wurde aber noch nicht initialisiert Ir-gendwie soll die Racer-Klasse ja die gleichenAttribute erhalten wie die Car-Klasse (speedacceleration etc) Dies geschieht in Zeile 3

Die Init-Methode wird hier deshalb explizit auf-gerufen damit als erster Parameter (self) eineReferenz auf das neue Racer-Objekt uumlbergebenwerden kann Nach der Initialisierung in Zeile 3hat das Racer-Objekt also die Attribute speedmax_speed und acceleration mit den Werten0 400 und 10 Auszligerdem noch das das Attributcolor

In Zeile 4 erhaumllt die Klasse Racer ein zusaumltzli-ches Attribut Da die Wagen der Racer-Klasseeine manuelle Schaltung erhalten sollen wirdder gerade eingelegte Gang an das Attributcurrent_gear gebunden

Die Bedeutung der drei Funktionen honk()shift_up() sowie shift_down() lassen sicham folgenden Beispiel gut ablesen

1 gtgtgt ferrari = Racer()2 gtgtgt ferrarihonk()3 Tuuuuut Tuuuut Tuuuut4 gtgtgt ferrarishift_up()5 Current gear 16 gtgtgt ferrariaccelerate()7 Current Speed 108 gtgtgt ferrariaccelerate()9 Current Speed 2010 gtgtgt ferrarishift_up()11 Current gear 2

Zunaumlchst wird also eine Instanz der KlasseRacer erstellt und an den Namen ferrari ge-bunden Der Aufruf der Methode honk() fuumlhrtbei Instanzen der Car-Klasse zur Ausgabe vonTuuuuut Tuuuut ndash bei Instanzen der Racer-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 26

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Klasse aber zu Tuuuuut Tuuuut Tuuuut DieMethode honk() der Klasse Racer uumlberschreibtalso die gleichnamige Methode der Elternklasse

In den Zeilen 4 und 10 kommen die neuen Metho-den shift_up() und shift_down() ins SpielSie erweitern die urspruumlngliche Car-Klasse alsoIn den Zeilen 6 und 8 wiederum wird deutlichdass die Methode accelerate() offensichtlichvon der Car-Klasse geerbt wurde Obwohl sie inder Racer-Klasse nicht definiert wurde steht siezur Verfuumlgung und zeigt das gleiche Verhaltenwie in der Car-Klasse

Es lassen sich noch viele andere Beispiele fin-den in denen Klassen und Vererbungen sinn-voll zur Modellierung von Datenstrukturen ein-gesetzt werden koumlnnen Etwa in einem Rollen-spiel Hier wuumlrde es vermutlich zunaumlchst die Ba-sisklasse Charakter geben ndash diese haumltte Attribu-te wie Kampfkraft Leben oder SchnelligkeitDarauf aufbauend gaumlbe es Klassen wie MagierKaumlmpfer oder Drache die jeweils unterschiedli-che Attribute haben (mehr oder weniger Lebens-energie etc) und ganz eigene Methoden wiezaubere() oder spucke_feuer() implementie-ren

Klassen im EinsatzNatuumlrlich sind weder Renn- noch Rollenspieleder primaumlre Einsatzzweck von Python Es sindlediglich sehr anschauliche Beispiele dafuumlr wieman ausgehend von einer Basisklasse verschie-dene andere Klassen entwirft

Ein etwas praxisnaumlheres Beispiel koumlnnte aber ei-ne Musikverwaltung sein Auch hier stellt sich dieFrage Wie laumlsst sich die Funktionalitaumlt die imple-mentiert werden soll sinnvoll strukturieren und inKlassen kapseln

Eine Musikverwaltung arbeitet in aller Regelmit einer Datenbank Hier waumlre eine eigeneKlasse sicher sinnvoll Sie haumltte Methoden wieerstelle_datenbank() finde_lied() odertrage_lied_ein() Der Vorteil dabei Wenn imgesamten Programm diese Schnittstellen der Da-tenbankklasse benutzt werden kann sehr leichteine zweite Datenbankklasse umgesetzt wer-den ndash etwa um auch die Unterstuumltzung vonPostgreSQL anzubieten Da die Schnittstellen (al-so die verfuumlgbaren Methoden und Attribute) beibeiden Klassen identisch sind ist ein Austauschohne Probleme moumlglich

Weiterhin benoumltigt die Musikverwaltung ei-ne grafische Benutzeroberflaumlche (GUI)Auch diese wuumlrde in eine Klasse ge-kapselt werden und haumltte Methoden wiezeige_dialog() knopf_wurde_geklickt()oder eingabefeld_wurde_veraendert()Wenn die GUI-Klasse gut entworfen wird kannspaumlter theoretisch sehr leicht eine Ersatz-GUIentwickelt werden etwa wenn doch Qt [3] stattGTK [4] verwendet werden soll

Diese Beispiele lassen sich leicht fortspin-nen Die Anbindung an lastfm (Attribu-te benutzername passwort Methodenuebertrage_gerade_gespieltes_lied()

oder anmelden()) lieszlige sich sicher ebenso sinn-voll in einer Klasse kappseln wie etwa ein Cover-Downloader fuumlr Amazon

Alles ist eine KlasseIn Python haben Klassen aber eine noch vielgrundlegendere Bedeutung denn alle Datenty-pen (Zeichenketten Ganz- und Flieszligkommazah-len Listen oder Dictionaries) sind in Python Ob-jekte Es gibt also auch entsprechende Klassenvon denen geerbt werden kann

class BoxiCode(unicode)def boxify(self)

text_with_borders = u= 0y=format(self)line = len(ytext_with_borders) u=

output = u0n1n2yformat(line ytext_with_borders line)return output

Listing 3 BoxiCodepy

Hier etwa wird von der Klasse unicode geerbtUnicode-Objekte koumlnnen wie folgt erstellt wer-den

gtgtgt unicode(Hallo)uhallo

Aus dieser Einfuumlhrung ist folgende Kurzschreib-weise bereits bekannt

gtgtgt uHallouHallo

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 27

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

In beiden Faumlllen wird also eine Unicode-Zeichenkette mit dem Inhalt Hallo erstellt BeideAufrufe sind aumlquivalent

Die neue Klasse BoxiCode erbt von unicodeund fuumlgt lediglich eine Methode hinzu das altbe-kannte boxify()

gtgtgt s=BoxiCode(Hallo Welt)gtgtgt suHallo Weltgtgtgt slower()uhallo weltgtgtgt print sboxify()=============== Hallo Welt ===============

BoxiCode hat offensichtlich saumlmtliche Methodenvon unicode geerbt ndash im Beispiel an der Me-thode lower() zu sehen Zusaumltzlich gibt es nunaber eine Methode um den jeweiligen Text in ei-ner ASCII-Box anzeigen zu lassen

Auf gleiche Weise koumlnnte man eine Listenklasseerstellen die zusaumltzliche Methoden hat Oder ei-ne Ganzzahlklasse mit zusaumltzlichen Methoden

class SuperInt(int)def iseven(self)

return self 2 == 0

Die neue Klasse SuperInt erbt von int und hatdie neue Methode iseven() Diese gibt True zu-ruumlck wenn die jeweilige Zahl ohne Rest durch 2teilbar ist ( ist der Modulo-Operator)

1 gtgtgt x = SuperInt(15)2 gtgtgt y = SuperInt(16)3 gtgtgt xiseven()4 False5 gtgtgt yiseven()6 True7 gtgtgt z = x+y8 gtgtgt ziseven()9 Traceback (most recent call last)10 File ltstdingt line 1 in ltmodulegt11 AttributeError int object has no attribute iseven

In den Zeilen 1 und 2 werden zwei SuperInt-Objekte erstellt In den folgenden beiden Zeilenwird gezeigt dass die neue Methode wie erwar-tet arbeitet 15 ist ungerade 16 gerade In Zei-le 7 werden die beiden SuperInt-Objekte ad-diert und das Ergebnis wird an den Namen z ge-bunden Beim Versuch auch hier die Methodeiseven() aufzurufen kommt es zu einem Feh-ler

Offensichtlich handelt es sich bei dem neu erstell-ten Objekt das die Summe von x und y reprauml-sentiert nicht mehr um ein von SuperInt abge-leitetes Objekt Es handelt sich um ein einfachesint-Objekt und das kennt die Methode iseven()nicht

Das haumlngt mit der Art und Weise zusammenwie Python Ganzzahl-Objekte addiert Der Auf-ruf 3 + 5 wird intern als 3__add__(5) inter-pretiert Es wird also die Methode __add__ derlinks stehenden Ganzzahl aufgerufen Diese Me-thode erstellt ein neues int-Objekt Soll nun

stattdessen ein neuesSuperInt-Objekt er-stellt werden muumlsstedie Methode __add__in der SuperInt-Klasse neue imple-mentiert werden Daes aber auch Metho-den fuumlr SubtraktionDivision Multiplikatio-nen und viele anderemathematische Ope-

rationen gibt wuumlrde dies in viel (fehlertraumlchtige)Arbeit ausarten

Dieses Beispiel soll veranschaulichen dass dasErweitern von Datentypen in Python prinzipiellmoumlglich ist und manchmal durchaus sinnvoll seinkann Es kann aber auch zu schwer nachvollzieh-baren Fehlern fuumlhren und erschwert das Lesendes Quelltextes unter Umstaumlnden enorm Metho-den wie boxify() oder iseven() sollten daherimmer als unabhaumlngige Funktionen umgesetztwerden Fuumlr Anfaumlnger gilt also Nicht von Basis-datentypen erben keine bdquoSpezialmethodenldquo im-plementieren

Die Sache mit den UnterstrichenAttribute und Methoden in Python kommen bis-weilen mit allerlei Unterstrichen daher Es gibtdrei Varianten die dabei zu unterscheiden sindDa es dabei haumlufig zu Missverstaumlndnissen undFehlern kommt sollen die drei Varianten kurz an-geschnitten werden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 28

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Zwei Unterstriche vor und nach dem Metho-dennamen (__foo__)

Hierbei handelt es sich um spezielle Metho-den die auf bestimmte Aufrufe reagieren Sowurde bereits eroumlrtert dass der erste Aufrufvon MeineKlasse() die __init__-Methode derentsprechenden Klasse aufruft Die Methode__add__ wiederum wird aufgerufen wenn dieKlasse links von einem +-Operator steht

x = MeineKlasse()x + 3

wird also als

x = MeineKlasse()x__add__(3)

interpretiert Analog gibt es Methoden fuumlr vieleandere mathematische Operatoren Auch Opera-toren fuumlr Listen oder Dictionaries gibt es

x = MeineKlasse()x[test]

ruft intern etwa die Methode __getitem__() auf

x = MeineKlasse()x__getitem__(test)

Kurz und buumlndig erklaumlrt Von doppelten Un-terstrichen umgebene Methodennamen stehenfuumlr Spezialmethoden von Klassen Entsprechendsollten sie auch nur in diesem Zusammenhanggenutzt werden

Durch die Spezialmethoden ist es moumlglich eige-ne Klassen wie Listen Zeichenketten oder Zah-len agieren zu lassen bzw auf die entsprechen-den Operatoren zu reagieren Eine Uumlbersicht ver-schiedener Spezialmethoden findet sich in derPython-Dokumentation [5]

Einfacher Unterstrich vor Attributen(self_foo)

Hierbei handelt es sich lediglich um eine Konven-tion um ein Attribut als bdquoprivatldquo zu markieren Py-thon kennt aber ndash anders als andere Program-miersprachen ndash keine richtigen privaten Varia-blen [6] Daher ist ein einfacher Unterstrich eherals Hinweis oder als eine Erinnerung zu verste-hen ldquoDieses Attribut ist privat und kein Teil deroffiziellen Schnittstelleldquo

Doppelter Unterstrich vor Attributen houmlchs-tens ein Unterstrich danach (self__foo_bzw self__foo)

class Test(object)def __init__(self)

self_privat = halloself__sehrprivat = welt

class Test2(Test)def __init__(self)

Test__init__(self)self__sehrprivat = noch ein test

print self_Test__sehrprivatprint self_Test2__sehrprivat

Listing 4 TestKlassenpy

Auch hiermit werden private Variablen markiertIm Unterschied zum vorherigen Abschnitt wer-den diese aber zur Laufzeit umbenannt Die Um-benennung von Attributen mit zwei Unterstrichenwird in Python bdquoName Manglingldquo genannt [7]Durch diese Umbenennung soll verhindert wer-den dass Klassen die voneinander erben ver-sehentlich mit ihren Attributen die Attribute derElternklasse uumlberschreiben

gtgtgt x = Test2()weltnoch ein testgtgtgt x__dict___Test2__sehrprivat noch ein ytest _Test__sehrprivat welty_privat hallo

Das Spezialattribut __dict__ referenziert einDict mit allen Attributen einer Klasse Hierist deutlich zu erkennen was Name Mang-ling bewirkt den jeweiligen Attributen wird der

Klassenname vorangestellt So koumln-nen Eltern- und Kindklasse (Testund Test2 also) die gleichen Attributhaben ohne dass das Attribut der El-ternklasse uumlberschrieben wird

Aber auch das Name Mangling istkeine Sicherheitsfunktion Rein pri-vate Attribute gibt es in Python nicht

Bemerkungen und AusblickDie hier besprochenen Beispiele zei-gen einige der grundlegenden Me-chanismen von Klassen in Python

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 29

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

auf Python bietet daruumlber hinaus viele Erweite-rungen die die Klassen noch maumlchtiger und nuumltz-licher machen ndash etwa mehrfache Vererbung [8]oder sogenannte bdquoPropertiesldquo [9]

Im naumlchsten Teil dieser kleinen Einfuumlhrung sollder erste Grundstein fuumlr eine Musikdatenbank ge-legt werden Auch wird die Fehlerbehandlung inPython ein Thema sein

LINKS[1] httpdewikipediaorgwikiObjektorientierte_

Programmierung[2] httpneopythonicblogspotcom200810why-

explicit-self-has-to-stayhtml

[3] httpdewikipediaorgwikiQt[4] httpdewikipediaorgwikiGTK+[5] httpdocspythonorgreferencedatamodelhtml

basic-customization[6] httpdocspythonorgtutorialclasseshtmltut-

private[7] httpssecurewikimediaorgwikipediaenwiki

Name_manglingName_mangling_in_Python[8] httpwww5intumdepersonsferstlcpython_

kapitel_12_002htmmja9ad55f483dad0b289bb6a13fc9dd3fa

[9] httprealmikeorgblog20100718introduction-to-new-style-classes-in-python

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoLaser Pointerldquo copy by Randall Munroe (CC-BY-NC-25) httpxkcdcom729

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 30

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Python-Programmierung Teil 5 ndash In medias res von Daniel Noumlgel

Im vorherigen Teil dieser EinfuumlhrungbdquoPython-Programmierung Teil 4ldquo aufSeite 24 wurden Klassen besprochen Mit

diesem Teil soll nun ein Einstieg in die prakti-sche Programmierung in Angriff genommenwerden Zunaumlchst wird dazu aber noch auf dieFehlerbehandlung in Python eingegangen

FehlerbehandlungFehler werden in Python bdquoExceptionsldquo oder bdquoAus-nahmenldquo genannt Sie treten beispielsweise aufwenn eine Datei nicht geoumlffnet werden kann einSchluumlssel einer Liste abgefragt wird der gar nichtexistiert oder auf Variablen zugegriffen wird dienoch nicht bekannt sind Tritt ein Fehler auf wirddas Skript sofort beendet in der Konsole er-scheint eine Fehlermeldung

gtgtgt names = [uPeter uIsabell]gtgtgt namesremove(uKarla)Traceback (most recent call last)File ltstdingt line 1 in ltymodulegt

ValueError listremove(x) x not yin list

In diesem Beispiel wird zunaumlchst eine Listemit Namen definiert Beim Versuch den EintragKarla zu entfernen tritt ein Fehler auf da dieserEintrag sich gar nicht in der Liste befindet Umdiese spezielle Situation exakt zu erfassen wirftPython eine Ausnahme vom Typ ValueErrorPython kennt bereits in seiner Standardbibliothek

viele Typen von Fehlern [1] Zusaumltzlich gibt Py-thon noch die Zeilennummer und eine kurze Feh-lerbeschreibung aus

Nun ist es in den meisten Faumlllen nicht erwuumlnschtdass das Programm einfach endet und eine kryp-tische Fehlermeldung ausgibt Daher ist es moumlg-lich Stellen an denen Fehler auftreten koumlnnenmit folgender Syntax zu umgeben

1 try2 namesremove(uKarla)3 except ValueError4 print Eintrag nicht in der y

Liste

Bei dem Versuch die Anweisung in Zeile 2 aus-zufuumlhren wird wie oben auch ein ValueErrorausgeloumlst Statt aber das Programm abzubre-chen uumlberpruumlft der Interpreter ob einer derexcept-Ausdruumlcke (es sind mehrere moumlglich)diesen Fehler abfaumlngt Das ist in Zeile 3 der Fallder Programmfluss wird daher an dieser Stellefortgesetzt Grob lieszlige sich try exceptalso mit versuche im Fehlerfall mache uumlber-setzen

Es lassen sich auch mehrere moumlgliche Fehler ineinem except-Block abfangen

1 try2 namesremove(uKarla)3 except (SyntaxError ValueError) yas error

4 print uFolgender Fehler ist yaufgetreten 0format(errory)

Hier wird also auf einen moumlglichen SyntaxErrorebenso reagiert wie auf einen ValueErrorDas Konstrukt except as error fuumlhrt da-zu dass der aufgetretene Fehler im Rumpf desexcept-Blocks als error zur Verfuumlgung stehtSprich der aufgetretene Fehler wird an den Na-men error gebunden Obwohl Fehler-Objektekeine Zeichenketten sind koumlnnen sie in vielen Si-tuationen wie solche verwendet werden

gtgtgt error = ValueError(Hilfe)gtgtgt str(error)Hilfe

In Zeile 4 des zweiten Beispiels oben geschiehtim Grunde genommen Aumlhnliches Das Fehler-Objekt wird implizit in eine Zeichenkette umge-wandelt sodass auf der Konsole einige Detailszum Fehler erscheinen

else und finallyWie auch if-Bloumlcke kennen try except-Bloumlcke das else-Statement Der Rumpf eineselse-Blockes wird ausgefuumlhrt wenn im try-Block kein Fehler aufgetreten ist Der finally-Block schlieszliglich kommt immer zur Ausfuumlhrungegal ob ein Fehler auftrat oder nicht

l = [uPeter uIsabell]try

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 31

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

namesremove(uKarla)except ValueError

print Karla war gar nicht in yder Liste

elseprint Karla wurde aus der yListe entfernt

finallyprint Mir doch egal ob Karla yin der Liste war oder nicht

Abhaumlngig davon ob Karla nun in der Liste vor-handen war oder nicht erscheint entweder dieMeldung

Karla war gar nicht in der ListeMir doch egal ob Karla in der yListe war oder nicht

oder

Karla wurde aus der Liste entferntMir doch egal ob Karla in der yListe war oder nicht

Aumlsthetik des ScheiternsDie Ausnahmebehandlung in Python gilt als sehrmaumlchtig und wird oft gegenuumlber manuellen Testsbevorzugt Statt also von Hand zu uumlberpruumlfenob eine Datei vorhanden ist und ob der Benut-zer die noumltigen Rechte hat diese Datei zu lesenwuumlrde ein Python-Programmierer es einfach aufeinen Versuch ankommen lassen und die Dateioumlffnen Tritt ein Fehler auf wird dieser durch ent-sprechende except-Bloumlcke abgefangen und dortdarauf reagiert

Auftretende Fehler werden gewissermaszligen vonbdquounten nach obenldquo durchgereicht

1 def innen()2 return int(asd)34 def mitte()5 return innen()67 def aussen()8 return mitte()910 print aussen()

Der Umwandeln einer Zeichenkette wie asd in ei-ne Ganzzahl wird zu einem ValueError fuumlhrenDie Frage lautet nun An welcher Stelle muss die-ser Fehler nun abgefangen werden

Tatsaumlchlich gibt es theoretisch vier Moumlglichkei-ten Der Fehler tritt zunaumlchst in der Funktioninnen() auf Dort koumlnnte er mit einem tryexcept-Block abgefangen werden Wird derFehler dort nicht behandelt wird er an die uumlber-geordnete Funktion mitte() weitergegeben Ge-schieht auch hier keine Fehlerbehandlung uumlber-pruumlft der Interpreter ob auf naumlchsthoumlherer Ebene(also in aussen()) eine Fehlerbehandlung statt-findet Ist auch dies nicht der Fall besteht nochdie Moumlglichkeit den Fehler in Zeile 10 abzufan-gen Erst wenn an all diesen Stellen keine Feh-lerbehandlung stattgefunden hat bricht der Inter-preter die Abarbeitung des Skripts ab

Viele Anfaumlnger moumlgen sich jetzt die Frage stellenWieso vermeidet man nicht einfach Fehler indem

man dafuumlr sorgt dass die moumlglichen Ausnah-men nicht auftreten koumlnnen Folgender Code-Schnipsel soll das verdeutlichen

ein Dictionarypersons = uPeter m uKarlayw

irgend wo spaeter im Codeif uPeter in persons

gender = persons[uPeter]

Man koumlnnte an dieser Stelle natuumlrlich auch einenKeyError abfangen

trygender = persons[uPeter]

except KeyErrorpass

Diese kleine Situation zeigt das Dilemma Oft-mals ist nicht unbedingt klar und direkt ersichtlichwie man ein solches Problem optimal loumlst In Py-thon wird das EAFP-Prinzip (frei uumlbersetzt bdquoErstschieszligen dann fragenldquo) dem LBYL-Paradigma(in etwa bdquoSchau bevor du abspringstldquo) vorgezo-gen [2] EAFP wuumlrde offensichtlich fuumlr die zweiteVariante sprechen auszligerdem spricht dafuumlr dassdas Abklopfen aller Eventualitaumlten den Code un-ter Umstaumlnden sehr aufblaumlhen kann Anderer-seits handelt es sich bei Exceptions um Aus-nahmen Sollte es ndash wieso auch immer ndash sehrwahrscheinlich sein dass der Schluumlssel (obenPeter) an dieser Stelle nicht verfuumlgbar ist sowaumlre die Loumlsung mittels Test auf Vorhandenseinsinnvoller da es expliziter die erwartete Situati-

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 32

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

on ausdruumlckt Entscheidend fuumlr die gute Nutzungvon Exceptions ist also die Situation im CodeUumlber die Vor- und Nachteile von EAFP und LBYLhat Oran Looney einen sehr anschaulichen Arti-kel verfasst [3] Weitere allgemeine Hinweise zurFehlerbehandlung in Python finden sich in der of-fiziellen Dokumentation [4]

MusikverwaltungDie bisher vorgestellten Funktionen und Moumlglich-keiten Pythons sollen nun in einem etwas um-fassenderen Beispiel ndash einer kleinen Musikver-waltung ndash veranschaulicht werden In den naumlchs-ten beiden Teilen dieser Reihe wird dieses Bei-spiel dann sukzessive ausgebaut Den Anfangmacht ein kleines Modul das Verzeichnisse re-kursiv nach Musikdateien durchsucht und derenTags in einer Liste von Dictionaries abbildet

ID-Tags lesenZum Auslesen der ID3-Tags wird die Python-Bibliothek mutagen [5] eingesetzt In Ubuntu istes als Paket python-mutagen in den Paketquel-len verfuumlgbar und kann daruumlber bequem nachin-stalliert werden

Das Modul zum Auslesen der MP3s sollreadmp3s heiszligen Entsprechend wird die Dateireadmp3spy angelegt und mit folgenden Inhaltbefuumlllt

1 usrbinenv python2 codingutf-834 import os5 import sys

6 from mutageneasyid3 import yEasyID3

7 import mutagenmp38 import mutagenoggvorbis

Listing 1 readmp3s-importspy

Neben Shebang und Zeichenkodierung findensich hier fuumlnf Importe Das Modul os beinhal-tet diverse betriebsystemabhaumlngige Schnittstel-len Das Modul sys wird genutzt um auf die Pa-rameter des spaumlteren Programmes zugreifen zukoumlnnen Der Import

from mutageneasyid3 import EasyID3

erscheint zunaumlchst kompliziert Es wird aber le-diglich vom Submodul easyid3 des Paketesmutagen die Klasse EasyID3 in den Namens-raum des Skriptes importiert In Teil 3 dieserEinfuumlhrung (siehe freiesMagazin 122010 [6])wurde von derartigen from-Importen abgeratenmutagen wurde aber bewusst so geschriebendass das gezielte Importieren einzelner Sub-bibliotheken moumlglich und sinnvoll ist

Mit import mutagenmp3 wird schlieszliglich nochdas Submodul mp3 in den Namensraum desSkriptes importiert Der letzte Import verfaumlhrt pa-rallel mit dem Submodul oggvorbis Diese bei-den Module stellen spaumlter Schnittstellen zu denID3- bzw Vorbis-Tags bereit

Als naumlchstes wird eine Funktion implementiertwelche die Tags der Audiodateien ausliest undzuruumlckgibt Treten Fehler auf gibt die FunktionNone zuruumlck

1 def get_infos(path)23 path = pathdecode(utf-8)45 if pathlower()endswith(y

mp3)6 audio = mutagenmp3MP3(y

path ID3=EasyID3)7 else8 audio = mutagenoggvorbisy

OggVorbis(path)910 length = audioinfolength1112 infos = pathpath lengthy

length13 for tag in [title artisty

album]14 content = audioget(tagy

[None])[0]15 infos[tag] = content1617 return infos

Listing 2 readmp3s-get_infospy

Der Kopf der Funktion haumllt keine Uumlberraschun-gen bereit Die Funktion get_infos erwartet nureinen Parameter path In Zeile 3 werden diePfadangaben in Unicode umgewandelt ndash dies istschon allein in Hinblick auf die SQLite-Datenbankzu empfehlen Nun ist es immer moumlglich dass ei-nige Dateinamen fehlerhaft kodiert wurden (dasberuumlhmte Fragezeichen im Dateinamen) In die-sem Fall wuumlrde eine UnicodeDecodeError auf-treten Dieser Fehler wird in dieser Funktion nicht

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 33

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

behandelt daher muss er also an anderer Stelleabgefangen werden

In den Zeilen 6 und 8 wird abhaumlngig vonder Dateiendung (besser waumlre natuumlrlich ei-ne Uumlberpruumlfung des MIME-Types [7]) eineInstanz der Klasse mutagenmp3MP3 odermutagenoggvorbisOggVorbis erstellt Eswird jeweils der Pfad zur Audiodatei uumlbergebenDer Zusatz ID3=EasyID3 in Zeile 6 sorgt weiter-hin dafuumlr dass eine vereinfachte Schnittstelle aufdie ID3-Tags bereitsteht Die etwas kryptischenID3-Tags (TPE1 TALB) koumlnnen so einfach alsartist bzw album angesprochen werden Dieerstellten MP3- und OggVorbis-Objekte werdenjeweils an den Namen audio gebunden und ver-halten sich in vielerlei Hinsicht aumlhnlich So kannin Zeile 10 bequem die Dauer der Audiodateiausgelesen werden ndash unabhaumlngig davon umwelches Format es sich dabei handelt

In Zeile 12 wird zunaumlchst ein neues Dict mit demPfad und der Dauer erstellt und an den Nameninfos gebunden Die Schleife in Zeile 13 ite-riert uumlber die Liste mit Tags die ausgelesen wer-den sollen Die Objekte mutagenmp3MP3 undmutagenoggvorbisOggVorbis verhalten sichwie ein Dict sodass mit audio[title] der Ti-tel der jeweiligen Datei abgefragt werden koumlnn-te Da aber nicht jede Audiodatei zwangslaumlufigjedes Tag hat koumlnnten hier Fehler auftreten Andieser Stelle erspart man sich weitere Fehlerbe-handlungen und nutzt die get-Methode der DictsAllerdings gibt Mutagen nicht einfach Zeichenket-ten fuumlr die gewuumlnschten Tags aus Es werden

immer Listen zuruumlckgegeben sodass man stattKuumlnstlername die Liste [Kuumlnstlername] er-haumllt Daher wird am Ende von Zeile 14 mit[0] das erste Listenelement ausgelesen ndash dasist in den meisten Faumlllen voumlllig ausreichendHier lauert freilich auch eine kleine StolperfalleDie get-Methode arbeitet bekanntlich mit einemStandard-Parameter der zuruumlckgegeben wirdfalls das Dict den angefragten Wert nicht enthaumlltDieser Wert ist in dem Fall None Da die Ruumlck-gabe der get-Methode aber als Liste behandeltwird ([0]) muss auch None zunaumlchst in eine Lis-te gepackt werden

Nach dem Durchlaufen der Schleife wird in Zeile17 das Dict an die aufrufende Funktion uumlberge-ben

Hinweis In den Zeilen 10 und 14 wer-den audioinfolength und die Ruumlckgabevon audioget() an die Namen length bzwcontent gebunden Dieser Zwischenschritt dienthier lediglich der Veranschaulichung und derUumlbersichtlichkeit Fuumlr gewoumlhnlich wuumlrde man die-sen Zwischenschritt auslassen

pathpath lengthaudioinfolengthinfos[tag] = audioget(tag [None])[0]

Verzeichnis rekursiv auslesenNun wird noch eine Funktion benoumltigt die dieMP3s auf der Festplatte findet und an dieFunktion get_infos() uumlbergibt Diese Funktionsoll read_recursively() heiszligen (siehe naumlchs-te Seite)

Auch hier zunaumlchst nichts Neues Die Funktionkennt einen Parameter path Damit soll spaumlterdas Verzeichnis uumlbergeben werden welches re-kursiv durchsucht werden soll In Zeile 3 wird ei-ne Liste erstellt und an den Namen audio_infosgebunden Damit sollen spaumlter saumlmtliche vonget_infos() erzeugten Dicts mit den Tags auf-bewahrt werden counter und error_countersollen lediglich zaumlhlen wie viele Audiodateien be-reits verarbeitet wurden und bei wie vielen es zuFehlern kam

In Zeile 7 wird eine neue Funktion eingefuumlhrtoswalk() durchlaumluft ein Verzeichnis rekursivFuumlr jedes Verzeichnis gibt die Funktion dabeiden Pfad des aktuellen Verzeichnisses (root)die Verzeichnisse in dem aktuellen Verzeichnis(dirs) und die Dateien im jeweiligen Verzeichnis(files) an Bei oswalk() [8] handelt es sichum einen sogenannten Generator Das ist derGrund warum die Funktion in einer Schleife ver-wendet werden kann [9]

Da fuumlr die Musikdatenbank nur Dateien von In-teresse sind genuumlgt es in Zeile 8 uumlber die

Dateiliste eines jeden Verzeichnisses zuiterieren In Zeile 9 wird fuumlr jede Da-tei gepruumlft ob sie mit mp3 oder oggendet Da die Dateien theoretisch auch

auf mP3 oder OgG enden koumlnnten werden dieDateinamen in Kleinbuchstaben verglichen Auchhier gilt Die Abfrage der Dateiendung ist keinesonderlich befriedigende Loumlsung ndash fuumlr das erstekleine Projekt sollte dieses Vorgehen aber ausrei-chend sein

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 34

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

1 def read_recursively(path)23 audio_infos = []4 counter = 05 error_counter = 067 for root dirs files in oswalk(path)8 for fl in files9 if fllower()endswith(mp3) or fllower()endswith(ogg)10 path = ospathjoin(root fl)11 try12 infos = get_infos(path)13 except (UnicodeDecodeError mutagenmp3HeaderNotFoundError y

mutagenid3error) as inst14 print unnSKIPPING reading this file15 print path16 print uERROR 0nformat(inst)17 error_counter +=118 else19 audio_infosappend(infos)20 counter +=121 print rScanning 0 Files ok 1 Files brokenformat(countery

error_counter)22 print23 return audio_infos

Listing 3 readmp3s-read_recursivelypy

Da in der Liste files nur Dateinamen vor-handen sind wird in Zeile 10 der komplettePfad erzeugt Die Funktion ospathjoin()kuumlmmert sich dabei darum dass die korrek-ten Schraumlgstriche verwendet werden (Linux undWindows unterscheiden sich in dieser Hinsicht)Durch ospathjoin wird weiterhin sicher ge-stellt dass es nicht versehentlich zu doppel-ten Schraumlgstrichen im Pfad kommt In Zeile 12

schlieszliglich wird der so erstellte Pfad an die Funk-tion get_infos() uumlbergeben und das Ergebnisan den Namen info gebunden

In Zeile 11 wird aber zunaumlchst die Fehlerbe-handlung eingeleitet Die in der Funktion get_infos() nicht behandelten Fehler sollen alsoan dieser Stelle abgefangen werden In Zeile13 werden dabei drei Fehler erwartet Der be-

reits erwaumlhnte UnicodeDecodeError findet sichebenso wie die Fehler mutagenmp3HeaderNotFoundError und mutagenid3error Waumlh-rend der UnicodeDecodeError auftritt wenn ei-ne Zeichenkette nicht in Unicode umgewandeltwerden konnte und damit fuumlr die spaumlter verwen-dete SQLite-Datenbank ungeeignet ist tritt derHeaderNotFoundError auf wenn Mutagen in ei-ner MP3-Datei keinen MP3-Header finden konn-te mutagenid3error schlieszliglich faumlngt vieleweitere Mutagen-Fehler ab Dies erspart demEntwickler die Arbeit jeden einzelnen moumlglichenFehler im Vorfeld zu definieren Ihm genuumlgt es(wie in diesem Beispiel) zu wissen dass Muta-gen ndash warum auch immer ndash eine bestimmte Dateinicht verarbeiten konnte

In den Zeilen 14-17 findet die Fehlerbehandlungstatt Der Fehler wird in der Konsole ausgege-ben und der Fehlerzaumlhler wird um 1 erhoumlht Nurwenn kein Fehler auftritt (Zeile 18) wird in Zei-le 19 die Ruumlckgabe von get_infos() an die Lis-te audio_infos angehaumlngt In diesem Fall wirdcounter um 1 erhoumlht

Eine letzte Besonderheit findet sich in Zeile 21Die Zeichenkette dort beginnt mit r DieseEscape-Sequenz steht fuumlr den sogenannten Wa-genruumlcklauf Dadurch wird der Cursor im Ter-minal wieder auf den Anfang der Zeile gesetztNachfolgende Ausgaben mit print wuumlrden dievorherigen Ausgaben uumlberschreiben Da printeine Zeile im Normalfall mit einem Zeilenum-bruch abschlieszligt wird dies hier mit dem Kommaam Ende der Zeile unterbunden

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 35

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Der Wagenruumlcklauf und das Unterbinden des Zei-lenumbruches sorgen dafuumlr dass die Konsolenicht mit Textausgaben uumlberflutet wird Die An-zahl der verarbeiteten Dateien wird immer in dieselbe Zeile geschrieben

Hinweis In Python gt= 3 muumlsste Zeile 17 wiefolgt aussehen

print (rScanning 0 MP3s ok y1 MP3s brokenformat(counter yerror_counter) end=)

Kleiner TestObwohl die Musikdatenbank noch in weiter Fer-ne ist kann zumindest das Modul readmp3sschon einmal getestet werden Dazu wird am En-de des Skripts noch folgende if-Block ergaumlnzt

1 if __name__ == __main__2 try3 path = sysargv[1]4 except IndexError5 print Usage6 print readmp3spy y

DIRECTORY7 else8 mp3s = read_recursively(y

path)

Listing 4 readmp3s-mainpy

Die erste Zeile uumlberpruumlft dabei ob die Dateials Modul oder als eigenstaumlndiges Skript gela-den wurde Nur im letzteren Fall kommen die fol-genden Zeilen zur Ausfuumlhrung In Zeile 3 wirdder zweite Parameter des Skriptes aus der Lis-te sysargv gelesen Der erste Parameter ist

immer der Dateiname (hier also readmp3spy)Falls der Benutzer keine Parameter uumlbergebenund die Liste nur einen Eintrag hat wird es inZeile 3 zu einem IndexError kommen der aberin Zeile 4 abgefangen wird Im Fehlerfall wirddann ein kurzer Hinweis auf den erforderlichenParameter gegeben In Zeile 8 wird die Funk-tion read_recursively() aufgerufen Wie be-reits erwaumlhnt kommt dieser else-Block nur zurAusfuumlhrung wenn zuvor kein Fehler auftrat

Das Skript wird nun das angegebene Verzeichnisdurchsuchen die ID-Tags auslesen und uumlber dieAnzahl lesbarer und nicht-lesbarer MP3s infor-mieren Keine Sorge Das Skript ist bisweilen et-was empfindlich was die Zeichenkodierung unddie MP3-Header betrifft und nicht alle als defektgemeldeten MP3s sind tatsaumlchlich unbrauchbar

Im naumlchsten Teil dieser Reihe wird die Musikda-tenbank um eine SQLite-Datenbank ergaumlnzt

LINKS

[1] httpdocspythonorglibraryexceptionshtml[2] httpdocspythonorgglossaryhtml[3] httporanlooneycomlbyl-vs-eafp[4] httpdocspythonorgtutorialerrorshtml[5] httpcodegooglecompmutagen[6] httpwwwfreiesmagazindefreiesMagazin-2010-

12[7] httpdewikipediaorgwikiMIME-Type[8] httpdocspythonorglibraryoshtmloswalk[9] httpdewikibooksorgwikiPython-Programmier

ung_FunktionenGeneratoren_und_yield

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

bdquoTheft of the Magildquo copy by Randall Munroe(CC-BY-NC-25) httpxkcdcom506

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 36

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung von Daniel Noumlgel

Im fuumlnften Teil dieser Reihe bdquoPython-Programmierung Teil 5ldquo auf Seite 24 wur-de die Fehlerbehandlung in Python be-

sprochen Daruumlber hinaus wurde der Grund-stein fuumlr eine kleine Musikverwaltung gelegtBisher wurden Funktionen implementiert dieein gegebenes Verzeichnis rekursiv nach Mu-sikdateien durchsuchen und dann deren ID-Tags auslesen In diesem Teil soll die Musik-verwaltung um eine Datenbank erweitert wer-den die bequem durchsucht werden kann

Die DatenbankAls Datenbank wird in diesem Fall SQLite ein-gesetzt [1] Bei SQLite handelt es sich umein SQL-Datenbanksystem das ohne Server-Software auskommt und daher auch neben derSQLite-Programmbibliothek selbst keine weitereSoftware auf dem Zielrechner erfordert SQLiteunterstuumltzt viele SQL-Sprachbefehle ist aber ineinigen Bereichen simpler gehalten als beispiels-weise MySQL

Fuumlr die Verwendung in Python muss nebender SQLite-Programmbibliothek (sqlite3) nochdie entsprechende Python-Schnittstelle installiertwerden Diese findet sich in Ubuntu beispielswei-se im Paket python-sqlite2

Das im letzten Teil erstellte Python-Skript soll nunum eine Datenbankanbindung erweitert werdenWer bei den Ergaumlnzungen den Uumlberblick verliertkann das fertige Skript musicdbpy auch direkt

herunterladen und dort die Aumlnderungen nachvoll-ziehen

Die neuen ImporteZunaumlchst muumlssen einige Importe ergaumlnzt wer-den

import sqlite3

import subprocessfrom optparse import OptionParser

import codecsfrom textwrap import dedentfrom random import shuffle

Hier werden eine ganze Reihe neuer Module undFunktionen eingefuumlhrt sqlite3 stellt schlicht dieSchnittstelle zum SQLite-Datenbanksystem be-reit [2]

Bei subprocess handelt es sich um ein Modulmit dem aus Python heraus andere Prozesse ge-startet werden koumlnnen Daruumlber hinaus koumlnnenmit dem Modul Signale an diese Prozesse gesen-det werden oder STDOUT bzw STDERR ausge-lesen werden Auch das Schreiben nach STDINist mit subprocess moumlglich [3] In diesem Skriptwird es spaumlter lediglich benoumltigt um das Medien-wiedergabeprogramm Totem zu starten und eini-ge MP3s damit abzuspielen

Das Modul optparse haumllt verschieden Werkzeu-ge bereit um die Optionen und Argumente von

Skripten auszuwerten Auch lassen sich damitrecht einfach Uumlbersichten der moumlglichen Optio-nen erstellen [4]

Neu ist das Modul codecs [5] Mit dessen Funk-tion open() kann spaumlter bequem eine Datei miteiner beliebigen Zeichenkodierung geschriebenwerden

Die Funktion dedent aus dem Modul textwrapwird spaumlter im Skript dazu genutzt eine mehrzeili-ge eingeruumlckte Zeichenkette ohne Einruumlckungenausgeben zu koumlnnen [6]

Einzig das Modul random sollte aus den vorhe-rigen Teilen dieser Reihe bereits bekannt seinEs stellt verschiedene Zufallsfunktionen zur Ver-fuumlgung Die Funktion shuffle durchmischt einegegebene Liste schlicht sodass sich damit bei-spielsweise eine Wiedergabeliste durchmischenlieszlige [7]

Die DatenbankanbindungAls naumlchstes soll nun die Datenbankanbindungdes Skripts erstellt werden Diese besteht auszwei Klassen einer Cursor-Klasse und einerDatenbank-Klasse Beide Klassen verfuumlgen aberuumlber eine Neuerung die hier kurz erlaumlutert wer-den soll

Das Schluumlsselwort withDas Schluumlsselwort with ist ein Sprachelementdas es seit Python 25 gibt [8] Es wird beson-ders haumlufig beim Arbeiten mit Dateien eingesetzt

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 37

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

weshalb es auch an diesem Beispiel eroumlrtert wer-den soll Fuumlr gewoumlhnlich wird beim Umgang mitDateien ein Konstrukt wie das folgende benoumltigt

1 handler = open(dateitxt r)2 try3 print handlerread()4 finally5 handlerclose()

In Zeile 1 wird dabei eine Datei geoumlffnet und dasdaraus resultierende Datei-Objekt an den Namenhandler gebunden In Zeile 3 wird der Inhalt derDatei ausgegeben Der tryfinally-Blockstellt sicher dass das Datei-Objekt anschlieszligendin jedem Fall geschlossen wird ndash auch wenn beimAuslesen der Datei in Zeile 3 eventuell Fehleraufgetreten sind Die Konstruktion bdquoVorbereitenBearbeiten Aufraumlumenldquo ist allerdings auch in an-deren Zusammenhaumlngen so haumlufig anzutreffendass ab Python 25 mit with eine deutliche Ver-einfachung fuumlr derartige Faumllle eingefuumlhrt wurde

1 with open(dateitxt r) as yhandler

2 print handlerread()

Auch hier wird zunaumlchst die Datei opentxtzum Lesen geoumlffnet und das daraus resultieren-de Datei-Objekt an den Namen handler gebun-den Allerdings erfolgt die Zuweisung des Na-mens hier durch das Schluumlsselwort as In Zeile 2wird ndash wie gehabt ndash der Inhalt der Datei ausgege-ben Damit sind die beiden Schritte bdquoVorbereitenldquound bdquoBearbeitenldquo abgehandelt Das Aufraumlumenerfolgt mit dem Verlassen der Kontrollstruktur al-

so am Ende des with-Blocks Es muss nicht ex-plizit durchgefuumlhrt werden Wie funktioniert das

Seit Python 25 gibt es zwei neue speziel-le Methoden die Objekte implementieren koumln-nen __enter__ und __exit__ Die Methode__enter__ ist fuumlr die Vorbereitung zustaumlndig undwird implizit beim Betreten des with-Blocks auf-gerufen Der Ruumlckgabewert dieser Methode wirddann an den mit as angegebenen Namen gebun-den ndash oben also an den Namen handler Die Me-thode __exit__ wird beim Verlassen des with-Blocks aufgerufen ndash unabhaumlngig davon ob einFehler aufgetreten ist oder nicht

Diese Konstruktion wird im Folgenden auchfuumlr die Datenbank verwendet Da die SQLite-Schnittstelle fuumlr Python von Haus aus noch kei-nen Gebrauch von diesem neuen Sprachmittelmacht wird es hier selbst implementiert

Der CursorSQLite kennt sogenannte Cursor mit denen Da-tensaumltze in Datenbanken ausgelesen und bear-beitet werden [9] In der Regel folgt auch dasArbeiten mit Cursorn dem Schema bdquoVorbereitenBearbeiten Aufraumlumenldquo weshalb sich hier dieVerwendung der with-Anweisung empfiehlt

Das bisherige Skript aus dem letzten Teil wirdnun um diese Klasse ergaumlnzt

class Cursor(object)def __init__(self connection)

selfconnection = connection

def __enter__(self)selfcursor = selfconnectioncursor()return selfcursor

def __exit__(self type value traceback)selfcursorclose()

Es handelt sich hierbei letztlich nur um einensogenannten bdquoWrapperldquo der die Verwendungvon SQLite-Cursorn mit with ermoumlglicht Spaumlterkoumlnnte ein Aufruf wie folgt aussehen

1 with Cursor(connection) as cursor2 cursormachetwas()

connection ist dabei eine Referenz auf einSQLite-Connection-Objekt das eine offene Da-tenbankverbindung verwaltet In Zeile 1 wirdnun zunaumlchst eine Instanz der Cursor-Klasseerstellt und connection als Parameter uumlberge-ben In der Methode __init__ wird die Re-ferenz auf das Connection-Objekt an das At-tribut selfconnection gebunden Erst da-nach wird durch den with-Block die Metho-de __enter__ des Cursor-Objektes aufgerufenHier wird nun das SQLite-Cursor-Objekt durchden Aufruf selfconnectioncursor() erstelltDas Objekt wird an das Attribut selfcursor ge-bunden und mittels des Schluumlsselwortes returnzuruumlckgegeben Da der Ruumlckgabewert der Me-thode __enter__ in with-Bloumlcken an den hinteras angegebenen Namen gebunden wird stehtnun eine Referenz auf das SQLite-Cursor-Objektunter dem Namen cursor zur Verfuumlgung In Zeile2 des Beispiels kann so bequem auf das SQLite-Cursor-Objekt zugegriffen werden Mit dem Ver-lassen des with-Blocks wuumlrde schlieszliglich dieMethode __exit__ aufgerufen werden in derdas Cursor-Objekt korrekt geschlossen wird

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 38

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

Noch einmal zur Erinnerung Die Cursor-Klassefungiert hier lediglich als Wrapper Ohne sie saumlhejeder Zugriff auf SQLite-Cursor wie folgt aus

cursor = connectioncursor()try

cursormachetwas()finally

cursorclose()

Die DatenbankklasseAls naumlchstes soll nun die eigentliche Daten-bankanbindung realisiert werden Aus Gruumlndender Uumlbersichtlichkeit wird der Code nur verlinktmusicdb-databaseconnectorpy

Auch die Klasse DatabaseConnector kennt dieMethode __enter__ und __exit__ und ist da-mit auf die Verwendung mit with ausgelegt Inder Methode __enter__ werden dabei alle rele-vanten Vorbereitungen fuumlr die Erstellung der Da-tenbankverbindung getroffen Zunaumlchst wird derOrdner des Skriptes per

ospathdirname(ospathabspath((y__file__)))

ermittelt und an den Namen path gebunden [10]In dem selben Ordner soll nun auch die Daten-bank musicdb abgelegt werden Existiert dieseDatenbank noch nicht wird sie mit der Methodecreate_database() erstellt Anschlieszligend wirddurch den Aufruf

selfconnection = sqlite3connect(yselfdb_path)

eine Verbindung zu dieser Datenbank erstelltund das Connection-Objekt an das Attributselfconnection gebunden Mit der Zeile

selfconnectionrow_factory = ysqlite3Row

wird SQLite angewiesen die Ergebnisse in Formvon Row-Objekten [11] darzustellen Dadurchlassen sich die Ergebnisse von Datenbankabfra-gen spaumlter sehr leicht uumlber Schluumlsselworte an-sprechen

Neben der Methode __enter__ die die Da-tenbankverbindung schlicht wieder schlieszligtkennt die Klasse DatabaseConnector weiterhinnoch die Methoden search get_all_songsinsert_songs und count Hier werden je-weils verschiedene Datenbankzugriffe getaumltigtes kommt jeweils die oben vorgestellte Cursor-Klasse zum Einsatz Die Bedeutung der einzel-nen SQL-Abfragen sollte weitestgehend bekanntoder aus dem Kontext ersichtlich sein Im Rah-men dieser Einfuumlhrung ist eine tiefergehendeAuseinandersetzung mit der SQL-Syntax leidernicht moumlglich Eine Uumlbersicht der SQL-Befehlefindet sich allerdings auf der Homepage desSQLite-Projektes [12] Dennoch sollen einige An-merkungen zu kleinen Besonderheiten gemachtwerden

In der Methode search werden dem SuchbegriffProzentzeichen voran- und nachgestellt auszliger-dem werden Leerzeichen durch Prozentzeichenersetzt Hierbei handelt es sich lediglich um dieSQL-typischen Wildcard-Zeichen Am Ende der

Methode insert_songs findet sich auszligerdemdieser Aufruf

cursorexecutemany(sql songs)

Damit wird eine Liste von mehreren Liedern indie Datenbank eingetragen songs ist in diesemFall eine Liste von Tupeln nach folgendem Sche-ma

[(Kuenstler Titel Album yLaenge Pfad)(Kuenstler Titel Album yLaenge Pfad)]

So koumlnnen sehr effizient groszlige Mengen von Ti-telinformationen in die Datenbank uumlbernommenwerden Durch das anschlieszligende

connectioncommit()

werden die Aumlnderungen uumlbernommen DieserAufruf muss immer dann erfolgen wenn schrei-bend auf die Datenbank zugegriffen wurde An-sonsten waumlren die Aumlnderungen bei der naumlchstenAbfrage noch nicht sichtbar

Wiedergabelisten erstellen

Die Musikverwaltung soll in der Lage sein Lie-der die auf eine Suche passen in eine Wieder-gabeliste zu speichern Dazu wird das Skript umfolgende drei Funktionen erweitert

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 39

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

def generate_extended_playlist(songs)playlist = [uEXTM3Un]for id artist title album length path in songs

playlistappend(EXTINF01 - 2nformat(int(length) yartist

title))playlistappend(0nformat(path))

return ujoin(playlist)

def generate_simple_playlist(songs)return unjoin(hit[path] for hit in songs)

def dump(playlist path encoding=utf-8)with codecsopen(path w encoding=encoding) as fh

fhwrite(playlist)

Die ersten beiden Funktionen erwarten jeweilseine Liste songs Diese Songs sind im Fall die-ses Skripts eigentlich Row-Objekte wie sie vonder Methode search des DatabaseConnectorszuruumlckgegeben werden Diese Row-Objekte ver-halten sich dahingehend wie Listen und Dicts alsdass sie den Zugriff uumlber Schluumlsselworte ebensoermoumlglichen wie uumlber Listen-Indizes

Die Funktion generate_simple_playlist er-stellt nun schlicht eine Zeichenkette mit Pfadan-gaben getrennt durch einen Zeilenumbruch (n)Die Zeile mutet zunaumlchst recht kompliziert an

return unjoin(hit[path] for yhit in songs)

Dies ist aber nur eine kompaktere und effiziente-re Variante fuumlr folgenden Code (siehe AbschnittbdquoKleine Aufgabeldquo unten)

paths = []for hit in songs

pathsappend(hit[song])return unjoin(paths)

Die so erstellte Zeichenkette mit Pfandan-gaben kann als einfache m3u-Liste gespei-chert werden Sollen auszligerdem noch Meta-Informationen in der Wiedergabeliste gespei-chert werden muss eine erweiterte m3u-Liste er-stellt werden Dies geschieht durch die Funktiongenerate_extended_playlist Auch hier wirdeine Zeichenkette erstellt die spaumlter als Wieder-gabeliste gespeichert werden kann Allerdingswird dabei zunaumlchst der Umweg uumlber eine Listegegangen Jeder Eintrag in der Liste repraumlsen-tiert spaumlter eine Zeile Mit

playlist = [uEXTM3Un]

wird die Liste playlist direkt initial befuumlllt so-dass die spaumltere Wiedergabeliste in der erstenZeile die Information enthaumllt dass es sich um ei-ne erweiterte Wiedergabeliste handelt In der fol-genden for-Schleife werden die einzelnen Lie-der durchlaufen Fuumlr jedes Lied wird dabei einListen-Eintrag mit Meta-Informationen (EXTINF)und ein Listeneintrag mit der dazugehoumlrigenPfadangabe erstellt Erst in der letzten Zeile derFunktion wird aus der Liste playlist mit Hilfeder Methode join eine Zeichenkette die direktzuruumlckgegeben wird

Die dritte Funktion dump schreibt schlieszliglich ei-ne gegebene Zeichenkette (in diesem Fall dieWiedergabelisten) in eine Datei Statt der Funk-tion open kommt dabei allerdings die gleichna-mige Funktion aus dem Modul codecs zum Ein-satz Diese Funktion hat den Vorteil dass die ge-wuumlnschte Ziel-Kodierung direkt waumlhlbar ist (hierUTF-8)

StartbedingungenIn einem letzten Schritt wird nun der Block

if __name__ == __main__

komplett durch musicdb-mainpy ersetzt

Darin wird zunaumlchst eine Instanz des OptionPar-ser erzeugt und an den Namen parser gebun-den In den folgenden Zeilen werden die ver-schiedenen Optionen definiert die das Skriptspaumlter kennt Die Methode add_option fuumlgt je-weils eine weitere Option hinzu und definiert die

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 40

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

dazugehoumlrigen Schalter (beispielsweise -s oder--scan) das Schluumlsselwort uumlber das die Optionspaumlter ausgelesen wird (dest) und einen kurz-en Hilfetext (help) Es gibt eine Reihe weitererMoumlglichkeiten die einzelnen Optionen genauerzu spezifizieren (etwa metavar oder type vglDokumentation [13])

In der Zeile

options args = parserparse_args()

dieses Codeblocks wird der OptionParser mitparse_args() angewiesen die Parameter mitdenen das Skript gestartet wurde auszuwertenIm Ergebnis werden die Optionen an den Namenoptions gebunden die Argumente an den Na-men args

Ab der Zeile

if not (optionssearch or optionsyshuffle)

beginnt die Auswertung der Parameter Durchdiese und die danach folgenden drei Zeilen wirdeine Fehlermeldung ausgegeben wenn der Nut-zer die Schalter -t oder -p uumlbergeben hat ohnemit --shuffle oder --find eine Auswahl vonMusikstuumlcken zu treffen parsererror() gibtdie uumlbergebene Zeichenkette aus und beendetdas Skript

In der Zeile

with DatabaseConnector() as ydatabase

kommt die oben implementierte KlasseDatabaseConnector zum Einsatz sie wird imwith-Block an den Namen database gebun-den In der naumlchsten Zeile wird gepruumlft ob mitden Schaltern -s oder --scan ein Verzeich-nis uumlbergeben wurde Ist dies der Fall enthaumlltoptionsadd eine Pfadangabe als Zeichenket-te und der Ausdruck optionsadd ist wahr Indiesem Fall wird das angegebene Verzeichnismit der im letzten Teil umgesetzten Funktionread_recursively() ausgelesen Die so gefun-denen Titelinformationen werden mit der Metho-de insert_song() in die Datenbank geschrie-ben Anschlieszligend wird noch die Gesamtzahlder Titel in der Datenbank auf der Konsole aus-gegeben

Ab der Zeile

elif optionssearch or optionsyshuffle

werden die Optionen --shuffle und -f bzw--find behandelt Im Fall einer Suche wird dieZeichenkette zunaumlchst dekodiert und an denNamen searchterm gebunden Die so erstellteUnicode-Zeichenkette kann dann an die Such-funktion der Datenbank uumlbergeben werden DasErgebnis wird an den Namen songs gebundenIm Alternativfall ohne Suche wird eine Zufalls-funktion umgesetzt Dazu werden zunaumlchst mitget_all_songs() alle Lieder aus der Daten-bank ausgelesen und zufaumlllig angeordnet Durch

songs = songs[optionsshuffle]

wird dann ein Ausschnitt dieser Gesamtliste anden Namen songs gebunden Die Groumlszlige desAusschnitts hat der Benutzer zusammen mit derOption --shuffle uumlbergeben

Dieses Vorgehen ist natuumlrlich nicht besonders ef-fizient Wer 40000 Lieder in seiner Datenbankhat moumlchte sicher nicht dass alle diese Liederzunaumlchst ausgelesen werden nur um 100 zufaumllli-ge Lieder davon auszuwaumlhlen Sehr viel elegan-ter waumlre hier eine Loumlsung via SQL

SELECT FROM mp3s ORDER BY RANDOMy() LIMIT 20

Damit werden direkt 20 zufaumlllige Lieder aus derDatenbank ausgelesen Die im Skript umgesetz-te Variante sollte in der Praxis also eher nichteingesetzt werden Sie besticht aber in diesemFall dadurch dass sie durch die Verwendungdes random-Modules und die Nutzung von SlicesTechniken einsetzt die in den vorherigen Teilenbereits diskutiert wurden

In den beiden if-Bloumlcken von optionssearchwurden die ausgewaumlhlten Lieder jeweils an denNamen songs gebunden Fuumlr das weitere Vorge-hen ist es nicht von Bedeutung wie die Auswahlder Lieder zustande kam Mit if songs wird le-diglich gepruumlft ob uumlberhaupt Lieder ausgewaumlhltwurden (eine leere Liste wird als falsch ausge-wertet) Nachfolgend wird eine Wiedergabelisteerstellt und gespeichert falls die entsprechendOption optionsplaylist gesetzt wurde Dabeiwird der vom Nutzer angegebene Dateiname um

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 41

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

PROGRAMMIERUNG

m3u erweitert falls er nicht darauf endet Am En-de kommt das subprocess-Modul zum Einsatzwenn die Option -t bzw --totem gesetzt wur-de Die Funktion Popen die Totem ausfuumlhren sollerwartet die Parameter in Form einer Liste Die-se wird initial mit totem und --enqueue befuumllltund danach um die Pfadangaben der ausgewaumlhl-ten Musikstuumlcke erweitert So entsteht eine Listenach dem Schema

[totem --enqueue song1mp3ysong2mp3]

Die entsprechende Befehlszeile in der Shell saumlhewie folgt aus

$ totem --enqueue song1mp3 song2mp3

Totem wird also mit der Option --enqueue ge-startet die alle folgenden Musikstuumlcke an dieWiedergabeliste anhaumlngt

Schlieszliglich deckt dieser Codeblock noch zweiweitere Eventualitaumlten ab Der vorletzte else-Block ist von Bedeutung wenn die Liste songsleer ist also beispielsweise die Suche keine Tref-fer ergab Das letzte else gibt einen Nutzungs-hinweis auf der Konsole aus wenn weder die Op-tionen add search noch shuffle gesetzt wur-den

Kleine AufgabeMehrfach wurden kleine Fragestellungen oderAufgaben erbeten die die Leser bis zum Er-scheinen des naumlchsten Teils zu loumlsen haumltten Im

Abschnitt bdquoStartbedingungenldquo oben wird auf ei-ne moumlgliche Alternative zur derzeitigen Shuffle-Funktion hingewiesen Interessierte Leser koumlnn-ten versuchen die dort vorgeschlagenen Aumlnde-rungen zu implementieren indem sie die Klas-se DatabaseConnector um eine shuffle()-Methode erweitern und das Skript so anpassendass diese Methode statt der jetzigen Variantezur Auswahl der Zufallstitel eingesetzt wird

Wer sich daruumlber hinaus noch vertiefend mit demSkript beschaumlftigen moumlchte kann sich die Funk-tion generate_simple_playlist einmal naumlheransehen Der Einzeiler im Funktionsrumpf hates bei naumlherer Betrachtung in sich dort kommtein sogenannter bdquoGenerator-Ausdruckldquo zum Ein-satz [14]

SchlusswortMit diesem sechsten Teil hat die einfuumlhrende Vor-stellung von Python eine kleine Zaumlsur erreichtIn Form der Musikdatenbank wurde mit Hilfe dervorgestellten Technologien und Werkzeuge erst-mals ein (etwas) groumlszligeres Projekt in Angriff ge-nommen das zum Experimentieren und Erwei-tern einlaumldt

Die Python-Reihe soll auch weiterhin fortgesetztwerden allerdings wird sich der Abstand der ein-zelnen Teile etwas vergroumlszligern und ab sofort vor-aussichtlich zweimonatlich erscheinen

LINKS[1] httpwwwsqliteorg[2] httpdocspythonorglibrarysqlite3html

[3] httpdocspythonorglibrarysubprocesshtml[4] httpdocspythonorglibraryoptparsehtml[5] httpdocspythonorglibrarycodecshtml[6] httpdocspythonorglibrarytextwraphtml

textwrapdedent[7] httpdocspythonorglibraryrandomhtml

randomshuffle[8] httpeffbotorgzonepython-with-statement

htm[9] httpdocspythonorglibrarysqlite3htmlsqlite3

Cursor[10] httpdocspythonorglibraryospathhtml

module-ospath[11] httpdocspythonorglibrarysqlite3htmlsqlite3

Row[12] httpwwwsqliteorglanghtml[13] httpdocspythonorglibraryoptparsehtml[14] httpwwwpythonorgdevpepspep-0289

Autoreninformation

Daniel Noumlgel (Webseite) beschaumlftigtsich seit drei Jahren mit Python Ihnuumlberzeugt besonders die intuitiveSyntax und die Vielzahl der unter-stuumltzten Bibliotheken die Python aufdem Linux-Desktop zu einem wahrenMultitalent machen

Diesen Artikel kommentieren

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 42

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum

MAGAZIN

ImpressumfreiesMagazin erscheint als PDF und HTML einmal monatlich

KontaktE-MailPostanschrift freiesMagazin

co Dominik WagenfuumlhrBeethovenstr 9171277 Rutesheim

Webpraumlsenz httpwwwfreiesmagazinde

ISSN 1867-7991Erscheinungsdatum 17 April 2011

RedaktionFrank Brungraumlber Thorsten SchmidtDominik Wagenfuumlhr (Verantwortlicher Redakteur)

Satz und LayoutRalf Damaschke Yannic HaupenthalNico Maikowski Matthias Sitte

KorrekturDaniel Braun Stefan FangmeierMathias Menzer Karsten SchuldtStephan Walter

VeranstaltungenRonny Fischer

Logo-DesignArne Weinberg (GNU FDL)

Dieses Magazin wurde mit LATEX erstellt Mit vollem Namen gekennzeichnete Beitraumlge geben nicht notwendigerweise die Meinung der Redaktion wieder Wenn Sie freiesMagazin ausdrucken moumlchten dann denken Sie bitte

an die Umwelt und drucken Sie nur im Notfall Die Baumlume werden es Ihnen danken -)

Soweit nicht anders angegeben stehen alle Artikel Beitraumlge und Bilder in freiesMagazin unter der Creative-Commons-Lizenz CC-BY-SA 30 Unported Das Copyright liegt beim jeweiligen Autor freiesMagazin unterliegt

als Gesamtwerk ebenso der Creative-Commons-Lizenz CC-BY-SA 30 Unported mit Ausnahme der Inhalte die unter einer anderen Lizenz hierin veroumlffentlicht werden Das Copyright liegt bei Dominik Wagenfuumlhr Es wird

erlaubt das Werkdie Werke unter den Bestimmungen der Creative-Commons-Lizenz zu kopieren zu verteilen undoder zu modifizieren Das freiesMagazin-Logo wurde von Arne Weinberg erstellt und unterliegt der GFDL

Die xkcd-Comics stehen separat unter der Creative-Commons-Lizenz CC-BY-NC 25 Generic Das Copyright liegt bei Randall Munroe

copy freiesMagazin CC-BY-SA 30 Python-Sonderausgabe 04-022011 43

  • Anleitungen
    • Python-Programmierung Teil 1 ndash Hallo Welt
    • Python-Programmierung Teil 2 ndash Tiefere Einblicke
    • Python-Programmierung Teil 3 ndash Funktionen und Module
    • Python-Programmierung Teil 4 ndash Klassenunterschiede
    • Python-Programmierung Teil 5 ndash In medias res
    • Python-Programmierung Teil 6 ndash Datenbank fuumlr die Musikverwaltung
    • Impressum