Arbeitsgruppe Programmiersprachen und Übersetzerkonstruktion Institut für Informatik Christian-Albrechts-Universität zu Kiel Seminararbeit Python 3 Ilonka Pingel Bugenhagenstr. 4 24114 Kiel [email protected] Stand: 22.02.2013 Betreuer: Priv.-Doz. Dr. Frank Huch Inhaltsverzeichnis 1 Einordnung 1.1 Geschichte und Entwicklung von Python 1.2 Unterstütze Programmierparadigmen . . 1.3 Anwendungsgebiet . . . . . . . . . . . . 1.3.1 Intention . . . . . . . . . . . . . 1.3.2 Einsatzbeispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 2 2 2 2 Konzepte und Struktur 2.1 Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.2 Modularisierung . . . . . . . . . . . . . . . . . . . . . . 2.1.3 Typisierung . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.4 Referenzen und Kopien . . . . . . . . . . . . . . . . . . 2.1.5 Beliebige Anzahl Parameter und Standardwerte . . . . . 2.2 Funktionale Programmierung . . . . . . . . . . . . . . . . . . . 2.2.1 Anonyme Funktionen und Funktionen höherer Ordnung 2.3 Objektorientierte Programmierung . . . . . . . . . . . . . . . . 2.3.1 Klassen und Vererbung . . . . . . . . . . . . . . . . . . 2.3.2 Magic Methods und Magic Attributes . . . . . . . . . . 2.4 Exceptionhandling . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Automatisiertes Testen . . . . . . . . . . . . . . . . . . . . . . . 2.5.1 Modul doctest . . . . . . . . . . . . . . . . . . . . . . . 2.5.2 Modul unittest . . . . . . . . . . . . . . . . . . . . . . 2.6 Besonderheiten . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6.1 Annotationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 4 4 6 7 7 8 8 12 12 13 14 15 17 17 3 Technische Unterstützung 3.1 Compiler, Interpreter und Entwicklungsumgebungen 3.2 Werkzeuge und Bibliotheken . . . . . . . . . . . . . . 3.2.1 Interaktive Umgebung . . . . . . . . . . . . . 3.2.2 Standardbibliothek . . . . . . . . . . . . . . . 3.3 Portabilität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 19 19 19 20 20 4 Zusammenfassung und Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 ii 1 Einordnung 1.1 Geschichte und Entwicklung von Python Die Programmiersprache Python wurde Anfang der 1990er Jahre von Guido van Rossum (* 31. Januar 1956) am Centrum voor Wiskunde en Informatica (CWI, s. http://www.cwi .nl) in Amsterdam entwickelt. Dieser hatte bereits bei der Entwicklung der Lehrsprache ABC mitgewirkt, die als Vorgängersprache von Python gilt. Auch Einflüsse aus C sind bei der Entwicklung von Python eingegangen. Dieser Einfluss findet sich unter Anderem in der umfassenden Standardbibliothek wieder, da die Funktionen des Moduls math den mathematischen Funktionen entsprechen, die vom C Standard definiert werden. Gedacht war Python ursprünglich für das verteilte Betriebssystem Amoeba (s. http:// www.cs.vu.nl/pub/amoeba) und als Lehrsprache, da sie durch ihre besonders simple Syntax sehr einfach zu erlernen sei. Der Name der Sprache geht zurück auf die englische Komikergruppe Monty Python, wodurch sich in der Dokumentation immer wieder Anspielungen auf Sketche von Monty Python finden lassen (z.B. heißen die traditionellen Variablen nicht foo oder bar, sondern spam oder eggs). Die erste Version von Python wurde 1991 veröffentlicht. 1995 hat van Rossum die Entwicklung von Python bei der Corporation for National Research Initiatives (CNRI, s. http://www.cnri.reston.va.us) in Reston, Virginia, fortgeführt, wechselte aber im Mai 2000 gemeinsam mit dem Python-Entwicklerteam zu BeOpen.com und gründete das BeOpen PythonLabs Team. Noch im Oktober desselben Jahres wechselten sie zu Digital Creations (heute Zope Corporation; s. http://www.zope.com). 2001 wurde dann die Python Software Foundation (PSF, s. http://www.python.org/psf) gegründet, welche bis heute Bestand hat und unter deren Namen bis dato die meisten Softwareversionen von Python veröffentlicht wurden (zuletzt 2012 Python 3.3.0). Alle Python-Versionen sind Open Source und unterliegen der PSF-Lizenz (s. http://www.docs.python.org/3/license. html). Hierbei ist zu erwähnen, dass sämtliche Python 3.x Versionen nicht mehr abwärtskompatibel zu früheren Versionen sind, es jedoch Methodiken gibt, älteren Python-Code in Python-3-Code zu überführen. Guido van Rossum ist bis heute, trotz der vielen Beiträge von anderen, hauptverantwortlicher Autor. In seinem Vorschlag "Computer Programming for Everybody" [1], den er 1999 an die DARPA (Defense Advanced Research Projects Agency, s. http://www. darpa.mil) sendete, hielt van Rossum seine Ziele für Python fest. So solle Python eine einfache, intuitive, aber dennoch eine sehr mächtige Sprache sein, deren Quelltext genauso einfach zu lesen sein soll wie die englische Sprache. Außerdem müsse sie dennoch für tägliche Aufgaben geeignet sein und kurze Entwicklungszeiten ermöglichen. Weiterhin 1 1 Einordnung war ihm immer wichtig, dass Python Open Source Software ist. Diese Ziele sind bis heute weitestgehend erreicht worden [1]. 1.2 Unterstütze Programmierparadigmen Python unterstützt gleich mehrere Programmierparadigmen, was stark zur großen Flexibilität der Sprache beiträgt. Sie ist in erster Linie eine imperative, dynamisch, streng getypte Programmiersprache, die objektorientiertes Programmieren unterstützt. Auch unterstützt Python einige funktionale Aspekte. Des Weiteren wird aspektoritiertes Programmieren ermöglicht und Python kann beziehungsweise wird häufig als Skriptsprache benutzt. 1.3 Anwendungsgebiet 1.3.1 Intention Aufgrund ihrer Flexibilität kann die Sprache Python sowohl für kleine als auch für große Programme eingesetzt werden (s. 1.3.2). Sie lässt sich als serverseitige Programmieroder auch als Skriptsprache verwenden und auch in eingebetteten Systemen kann sie problemlos eingesetzt werden. Es gibt beispielsweise viele Python-Interpreter für mobile Endgeräte wie Smartphones, PDAs etc. [2, S.30]. Die automatische Speicherverwaltung und die umfangreiche Standardbibliothek erlauben es, bereits mit kleinen Programmen komplexe Probleme zu beschreiben, daher wird Python oft für das (Rapid-)Prototyping eingesetzt. 1.3.2 Einsatzbeispiele Python wird in vielen unterschiedlichen Bereichen benutzt. So sind zum Beispiel WebAnwendungen wie GoogleMail (s. http://mail.google.com), YouTube (s. http://www .youtube.com), Dropbox (s. http://www.dropbox.com) oder Programme im Bereich des Filesharing (z.B. BitTorrent, s. http://www.bittorrent.com), aber auch Entwicklerwerkzeuge wie Mercurial (s. http://mercurial.selenic.com) oder Computerspiele (z.B. Civilization IV (s. http://www.2kgames.com/civ4/home.htm), Battlefield 2 (s. http:// www.battlefield.com/de/battlefield-2)) zumindest teilweise in Python implementiert. Python wird von anderen Anwendungen zur Anbindung von Erweiterungen als Skriptsprache unterstützt. Eine solche Unterstützung bieten beispielsweise die Grafikprogramme Blender (s. http://www.blender.org), Cinema 4D (s. http://www.maxon.net/de/products /cinema-4d-studio.html) oder auch GIMP (s. http://www.gimp.org). Ein weiteres Einsatzgebiet stellt die testgetriebene Entwicklung von Programmcode dar, denn Pythons Standardbibliothek stellt das Modul unittest zur Verfügung, welches die Funktionalitäten des aus Java bekannten Moduls JUnit implementiert. 2 2 Konzepte und Struktur 2.1 Grundlagen 2.1.1 Form Einzelne Python-Programme beziehungsweise -Module sind simpel aufgebaut. Wenn benötigt, werden zu Beginn des Codes Module oder Pakete mit der import-Anweisung geladen. Auf Funktionen eines Moduls kann dann per modulname.funktion zugegriffen werden. Dies sorgt dafür, dass sich gleichnamige Funktionen aus verschiedenen Modulen nicht gegenseitig behindern, denn so befinden sie sich jeweils in einem eigenen Namensraum. Anschließend kann direkt mit dem eigentlichen Code begonnen werden. Hierbei ist zu beachten, dass Python keinerlei Begrenzungszeichen wie Klammern oder Semikolons, wie man sie aus Sprachen wie C oder Java kennt, benutzt. Es wird lediglich über Zeilenumbrüche und Einrückungstiefen bestimmt, wo ein Ausdruck endet und zu welchem Kopf ein Ausdruck beziehungsweise ein ganzer Rumpf gehört (vgl. Syntax von Haskell). Das folgende Beispiel zeigt die funktional programmierte Fibonacci-Funktion. Fibonacci 1 2 3 4 5 6 7 def f i b ( n ) : i f ( n == 0 ) : return 1 e l i f ( n == 1 ) : return 1 else : r e t u r n f i b ( n−1) + f i b ( n−2) Code, der auf niedrigster Einrückungstiefe geschrieben wird und eine Anweisung ist, ist Code, der direkt ausgeführt wird, sobald die zugehörige Datei beziehungsweise das Modul ausgeführt wird. Man kann diesen also vergleichen mit Code, der in C oder Java in der main-Methode steht. Beim restlichen Code handelt es sich dann um Funktions- oder Klassendeklarationen, die innerhalb oder außerhalb des Moduls, sofern dieses importiert wird, benutzt werden können. Lokale Funktionen können definiert werden, indem sie innerhalb des Rumpfes einer anderen Funktion definiert werden, also auf entsprechender Einrückungstiefe unterhalb des Funktionskopfes bezüglich dessen die Funktion lokal sein soll. Kommentare werden als sogenannte Docstrings geschrieben. Hierfür wird der eigentlich Kommentar durch dreifache " oder ’ eingefasst. Solche Docstrings lassen sich zu Dokumentationszwecken bequem aus Python-Dateien auslesen. Zu beachten ist noch, 3 2 Konzepte und Struktur dass return-Statements immer optional sind, was beispielsweise in C anders aussieht, da dort konventionsgemäß beim problemlosen Ablauf der main-Methode immer die Zahl Null zurückgegeben wird. Dateien beziehungsweise Module, wie im Beispiel zu sehen, werden mit dem Namen modulname.py abgespeichert, in diesem Fall also form.py. 2.1.2 Modularisierung Wie bereits erwähnt, handelt es sich bei den einzelnen Python-Dateien jeweils um Module, die zu Paketen zusammengefasst werden können. Dies bietet die Möglichkeit, große Programme in mehrere Teile zu unterteilen, um so die Komplexität zu reduzieren, Übersichtlichkeit zu schaffen und den Abstraktionsgrad durch Kapselung von Programmcode zu erhöhen. Man unterscheidet hierbei zwischen sogenannten globalen und lokalen Modulen. Globale Module sind Module, die Datentypen und Funktionen bereitstellen, die einen thematisch abgegrenzten Zweck erfüllen. Dies sind also systemweit installierte Module, welche sich auch selbst erstellen lassen oder von Drittanbietern zur Installation bereit gestellt werden. Beispiele hierfür sind die Module der Standardbibliothek wie math oder random. Lokale Module hingegen dienen in erster Linie der Zerlegung eigenen Programmcodes in Teile mit unterschiedlichen Zuständigkeiten. Im Gegensatz zu den globalen Modulen sind diese Module allerdings nicht systemweit verfügbar, sondern müssen mit ihren absoluten oder relativen Pfaden eingebunden werden. 2.1.3 Typisierung Bei Python handelt es sich um eine streng getypte Programmiersprache mit dynamischer Typprüfung, wobei das sogenannte Duck-Typing verwendet wird. Das heißt erstens, dass die Typen von Variablen und Funktionen im Programmcode erst zur Laufzeit feststehen und zweitens, dass nicht die Klasse eines Objektes den Typ festlegt, sondern die Existenz von bestimmten Methoden und Attributen. Der Name Duck-Typing findet dabei seinen Ursprung in einem Gedicht von James Whitcomb Rileys. When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. [3] Typfehler treten also erst dann auf, wenn ein Operator auf Typen angewendet wird, für die er nicht definiert ist. Tritt ein solcher Fehler aufgrund von fehlerhaften Benutzereingaben auf, kann es sinnvoll sein, diese Eingaben mithilfe einer selbstgeschriebenen Typprüfung zu kontrollieren. Eine solche Typprüfung kann mittels Typ-Annotationen (s. Abschnitt 2.6.1) und Exceptionhandling (s. Abschnitt 2.4) realisiert werden. 2.1.4 Referenzen und Kopien In Python ist alles ein Objekt. Das heißt, auch die üblicherweise primitiven Datentypen wie int oder float sind Objekte, ebenso wie Exceptions, Funktionen oder Module. Der Typ ist aufgrund der dynamischen Typisierung an die Instanz selbst und nicht an die referenzierende Variable gebunden. Beim Benutzen selbiger ist jedoch Vorsicht geboten, 4 2 Konzepte und Struktur da hier unerwartete und vor allem ungewollte Nebenwirkungen durch Seiteneffekte bei mutablen Datentypen entstehen können. Python arbeitet, wenn dies nicht explizit anders angegeben wird, nur mit Referenzen auf Instanzen, was bei immutablen Datentypen keinerlei Problem darstellt, jedoch bei mutablen Datentypen zu kritischen Situationen führen kann, wie die folgenden Beispiele aus der interaktiven Python-Umgebung (s. Abschnitt 3.2) zeigen. Referenzen vs. Kopien - Beispiel 1 1 2 3 4 5 6 7 8 9 >>> >>> >>> [1 , >>> >>> [1 , >>> [1 , xs ys ys 2, xs xs 2, ys 2, = [1 ,2 ,3] = xs 3] += [ 3 , 4 , 5 ] 3 , 3 , 4 , 5] 3 , 3 , 4 , 5] Ein solches Verhalten kann insbesondere in sicherheitskritischen Bereichen zu unerwünschten Möglichkeiten führen. Wird mittels einer getter-Funktion die Referenz auf ein Attribut einer Klasse zurückgegeben, die einen mutablen Datentypen referenziert, ist es anschließend möglich, über diese Referenz auch das Klassenattribut zu verändern. Um solche Probleme zu umgehen, sollte man also im entsprechenden Kontext mit Kopien von Instanzen arbeiten. Dann würde das erste Beispiel von oben wie folgt aussehen. Referenzen vs. Kopien - Beispiel 1 (Korrektur) 1 2 3 4 5 6 7 8 9 >>> >>> >>> [1 , >>> >>> [1 , >>> [1 , xs ys ys 2, xs xs 2, ys 2, = [1 ,2 ,3] = xs [ : ] 3] += [ 3 , 4 , 5 ] 3 , 3 , 4 , 5] 3] Hierbei ist die Verwendung von xs[:] (vgl. Slicing [2, S. 123]) gleichbedeutend mit dem Ausdruck copy.copy(xs). Aber auch hier ist Vorsicht geboten. Beinhaltet nämlich der kopierte Datentyp wieder mutable Datentypen, so werden für diese erneut nur Referenzen erzeugt. Um auch für diese echte Kopien zu erhalten, muss die Funktion copy.deepcopy verwendet werden, welche rekursiv für alle beinhalteten Objekte Kopien 5 2 Konzepte und Struktur erzeugt. Man sollte jedoch nicht nur noch Kopien von Instanzen erzeugen, denn dies kann, gerade bei großen Datenmengen, zu einem sehr hohen Zeitaufwand und einem unnötig hohen Speicherbedarf eines Programms führen. Außerdem lassen sich mithilfe von copy.copy beziehungsweise copy.deepcopy nicht von allen Datentypen echte Kopien erzeugen. Module, Funktionen und wenige weitere Objekte werden durch Benutzung dieser Funktionen lediglich ein weiteres Mal referenziert [2, S. 522]. 2.1.5 Beliebige Anzahl Parameter und Standardwerte Python ermöglicht es sehr simpel, einer Funktion eine beliebige Anzahl von Parametern zu übergeben. Betrachte die Funktion, die das Produkt zweier Zahlen berechnet. Produkt zweier Zahlen 1 2 def p r o d ( a , b ) : r e t u r n a ∗b 3 4 5 6 ’ ’ ’ B e i s p i e l e f u e r Benutzung von p r o d ’ ’ ’ p r i n t ( prod (2 , 4)) p r i n t ( prod (6 , prod (42 , prod (21 , 9 ) ) ) ) Wie in den Beispielen zur Benutzung der Funktion prod zu sehen ist, ist es so kein Problem das Produkt zweier Zahlen zu berechnen. Möchte man nun das Produkt mehrerer Zahlen berechnen, so sehen die Aufrufe schnell sehr unschön aus und man muss für jeden zusätzlichen Faktor einen neuen Aufruf von prod erzeugen. Folgende Code-Erweiterung ist da schon deutlich besser. Produkt von zwei oder mehr Zahlen 1 2 3 4 5 def p r o d ( a , b , ∗ o t h e r ) : r e s u l t = a ∗b for i in other : r e s u l t ∗= i ; return r e s u l t 6 7 8 9 ’ ’ ’ B e i s p i e l e f u e r Benutzung von p r o d ’ ’ ’ p r i n t ( prod (2 , 4)) p r i n t ( prod (6 , 42 , 21 , 9)) Hier zeigt sich nun der Vorteil, dass nicht nur das Produkt von zwei, sondern auch das Produkt einer beliebigen Anzahl Zahlen berechenbar ist und zwar einfach durch das Hinzufügen weiterer Parameter. Der Parameter *other referenziert hier alle möglichen weiteren Parameter in Form eines Tupels. Es werden von der Funktion aber immer noch mindestens zwei Parameter gefordert. Mathematisch ist es möglich das leere oder 6 2 Konzepte und Struktur einstellige Produkt zu berechnen, daher ergibt sich folgende optimierte und sehr elegante Möglichkeit das Produkt beliebig vieler Zahlen zu berechnen. Produkt mehrerer Zahlen 1 2 3 4 5 def p r o d ( ∗ a r g s ) : result = 1 for i in args : r e s u l t ∗= i ; return r e s u l t 2.2 Funktionale Programmierung In Python ist es bequem möglich auch funktionale Programme zu schreiben. Das Beispiel aus Abschnitt 2.1.1 berechnet per Rekursion die n-te Fibonacci-Zahl. 2.2.1 Anonyme Funktionen und Funktionen höherer Ordnung Python stellt das Schlüsselwort lambda zur Verfügung, mit dessen Hilfe anonyme Funktionen deklariert werden können. Solche Ausdrücke werden meist für kleine, häufig benötigte Berechnungen benutzt. Das folgende Beispiel zeigt zwei Varianten die zweistellige prod-Funktion aus Abschnitt 2.1.5 mithilfe des lambda-Ausdrucks zu definieren. Die zweite Variante zeigt lediglich, dass Variablen anonyme Funktionen referenzieren können. Prod als lambda-Ausdruck 1 2 3 4 5 >>> ( lambda a , b : a ∗b ) ( 4 , 4 ) 16 >>> p r o d = lambda a , b : a ∗b >>> p r o d ( 4 , 4 ) 16 Solche anonymen Funktionen sind insbesondere bei der Verwendung von Funktionen höherer Ordnung nützlich. Hier werden einer Funktion eine oder mehrere Funktionen als Parameter übergeben. Dies ermöglicht es, sehr allgemeine und mächtige Funktionen zu schreiben. Im Folgenden sieht man eine solche Funktion höherer Ordnung anhand des Beispiels filter_list. Diese Funktion ist in der Lage aus beliebigen Listen diejenigen Elemente herauszufiltern, für die ein frei wählbares Prädikat p wahr wird. Somit wird die Wiederverwendbarkeit von filter_list erhöht, da es je nach Situation flexibel anpassbar ist. 7 2 Konzepte und Struktur filter_list 1 2 3 4 5 6 7 8 def f i l t e r _ l i s t ( p , l i s ) : def f i l t e r _ h e l p ( n ) : i f ( n >= l e n ( l i s ) ) : return [ ] e l i f (p( l i s [ n ] ) ) : r e t u r n [ l i s [ n ] ] + f i l t e r _ h e l p ( n+1) else : r e t u r n f i l t e r _ h e l p ( n+1) 9 10 return f i l t e r _ h e l p (0) Die folgenden Zeilen zeigen einige beispielhafte Verwendungsmöglichkeiten, wobei darauf zu achten ist, dass sich lambda-Ausdrücke nur innerhalb einer Zeile definieren lassen. Beispiele - filter_list 1 2 3 4 5 6 7 8 >>> f i l t e r l i s ( lambda n : n < 5 , [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 2 0 , 2 1 , 4 2 , 1 0 0 ] ) [1 , 2 , 3 , 4] >>> f i l t e r l i s ( lambda n : n % 2 == 0 , [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,20 ,21 ,42 ,100]) [ 2 , 4 , 6 , 8 , 20 , 42 , 100] >>> f i l t e r l i s ( lambda s t r : " i n " i n s t r , [ " I c h " , " b i n " , " e i n e " , " L i s t e " , " von " , " S t r i n g s " ] ) [ ’ bin ’ , ’ eine ’ , ’ Strings ’ ] 2.3 Objektorientierte Programmierung 2.3.1 Klassen und Vererbung Um in Python eigene Objekte erzeugen zu können, müssen für diese Konstruktionsschablonen, sogenannte Klassen, definiert werden. Als Beispiel dient hier die Klasse Vehicle mit den Attributen manufacturer, maxSpeed und currentSpeed sowie der Methode accelerate implementiert. Der Konstruktor heißt hier, wie auch in jeder anderen Klasse, __init__. Jede Methode, auch der Konstruktor, erwartet als erstes Argument die Referenz self auf sich selbst, wobei diese beim Verwenden der Methoden nicht explizit übergeben werden muss. Attribute werden im Allgemeinen nur innerhalb des Konstruktors definiert. Es gibt auch andere Wege dies zu tun, von diesen wird allerdings abgeraten, weshalb diese Möglichkeit hier nicht vorgestellt wird. 8 2 Konzepte und Struktur Klasse - Vehicle 1 2 3 4 5 6 7 class Vehicle : ’ ’ ’ Konstruktor ’ ’ ’ def __init__ ( s e l f , m a n u f a c t u r e r , maxSpeed ) : ’ ’ ’ G e s c h w i n d i g k e i t e n i n m/ s ’ ’ ’ s e l f . manufacturer = manufacturer s e l f . maxSpeed = maxSpeed self . currentSpeed = 0 8 9 10 11 12 13 14 ’ ’ ’ Methode zum B e s c h l e u n i g e n d e s V e h i k e l s , w o b e i a c c e l e r a t i o n i n m/ s ^2 und d u r a t i o n i n s e r w a r t e t werden . ’ ’ ’ def a c c e l e r a t e ( s e l f , a c c e l e r a t i o n , d u r a t i o n ) : newSpeed = s e l f . c u r r e n t S p e e d + d u r a t i o n ∗ a c c e l e r a t i o n i f ( newSpeed <= s e l f . maxSpeed ) : s e l f . c u r r e n t S p e e d = newSpeed Verwendet werden kann eine solche Klasse dann folgendermaßen. Verwendung der Klasse Vehicle 1 import v e h i c l e 2 3 4 5 v = v e h i c l e . V e h i c l e ( " Audi " , 1 4 ) v . a c c e l e r a t e (1 , 5) p r i n t ( " neue G e s c h w i n d i g k e i t : " , v . c u r r e n t S p e e d , "m/ s " ) 6 7 8 9 10 11 ’’’ Ausgabe a u f d e r K o n s o l e b e i A u s f u e h r u n g d i e s e s Moduls : l i l o @ l i l o −ACER: ~ $ p y t h o n v e h i c l e _ b s p . py neue G e s c h w i n d i g k e i t : 5 m/ s ’’’ Wie oben zu sehen ist, kann man auf die Attribute der Klasse Vehicle nicht nur von innerhalb der Klasse zugreifen, sondern auch von außen. Dies ist in vielen Fällen nicht gewünscht, sodass man setter- und getter-Methoden für solche Attribute schreibt, um kontrolliert auf sie zuzugreifen. In diesem Fall kann dann auch die Überprüfung bezüglich der Überschreitung der Maximal-Geschwindigkeit durch den setter durchgeführt werden, was stilistisch schöner ist und somit die Logik der accelerate-Methode sauber hält. Des Weiteren werden konventionsgemäß alle Attribute, auf die von außen nur per getter- beziehungsweise setter-Methode zugegriffen werden soll, mit einem Unterstrich beginnend geschrieben. Modifiziert man die Klasse Vehicle dahingehend, ergibt sich nun folgendes Bild, wobei hier zur Übersicht nur die getter und setter von currentSpeed ergänzt wurden. 9 2 Konzepte und Struktur Klasse - Vehicle - Erweiterung 1 2 3 4 5 6 7 class Vehicle : ’ ’ ’ Konstruktor ’ ’ ’ def __init__ ( s e l f , m a n u f a c t u r e r , maxSpeed ) : ’ ’ ’ G e s c h w i n d i g k e i t e n i n m/ s ’ ’ ’ s e l f . _manufacturer = manufacturer s e l f . _maxSpeed = maxSpeed s e l f . _currentSpeed = 0 8 9 10 def g e t C u r r e n t S p e e d ( s e l f ) : return s e l f . _currentSpeed 11 12 13 14 def s e t C u r r e n t S p e e d ( s e l f , newSpeed ) : i f ( newSpeed <= s e l f . _maxSpeed ) : s e l f . _ c u r r e n t S p e e d = newSpeed 15 16 17 18 19 20 ’ ’ ’ Methode zum B e s c h l e u n i g e n d e s V e h i k e l s , w o b e i a c c e l e r a t i o n i n m/ s ^2 und d u r a t i o n i n s e r w a r t e t werden . ’ ’ ’ def a c c e l e r a t e ( s e l f , a c c e l e r a t i o n , d u r a t i o n ) : s e l f . setCurrentSpeed ( s e l f . getCurrentSpeed () + duration ∗ acceleration ) Führt man nun allerdings den Beispiel-Code (s. Verwendung der Klasse Vehicle S. 9) aus (den direkten Zugriff hierbei um den nötigen _ ergänzt), stellt man fest, dass der Code fehlerlos terminiert. Das heißt, dass _currentSpeed immer noch nicht vor der Verwendung von außen geschützt ist. Es existiert lediglich eine Schnittstelle, über die das Attribut verwendet werden sollte, daher auch die konventionsgemäße Schreibweise mit _. Es gibt in Python keine Möglichkeit Attribute gegen Zugriffe von außen zu schützen. Ersatzweise verwendet man die sogenannten Property-Attribute. Ist für ein Attribut ein Property-Attribut deklariert, so wird bei jeder direkten Verwendung des PropertyAttributs implizit der getter- beziehungsweise setter-Methode gerufen. Um dies zu demonstrieren, wird zunächst die Klasse Vehicle modifziert und anschließend erneut der unveränderte Beispiel-Code ausgeführt. Wie im Folgenden zu sehen ist, werden nun trotz der intuitiven und scheinbar direkten Verwendung des Attributs currentSpeed die jeweiligen getter- und setter-Methoden verwendet. 10 2 Konzepte und Struktur Klasse - Vehicle mit Property-Attribut 1 2 3 4 5 class Vehicle : ... def g e t C u r r e n t S p e e d ( s e l f ) : print ( " getCurrentSpeed gerufen " ) return s e l f . _currentSpeed 6 7 8 9 10 11 12 def s e t C u r r e n t S p e e d ( s e l f , newSpeed ) : print ( " setCurrentSpeed gerufen " ) i f ( newSpeed <= s e l f . _maxSpeed ) : s e l f . _ c u r r e n t S p e e d = newSpeed ... currentSpeed = property ( getCurrentSpeed , setCurrentSpeed ) 13 14 15 16 17 18 19 20 ’’’ Ausgabe a u f d e r K o n s o l e : getCurrentSpeed gerufen setCurrentSpeed gerufen getCurrentSpeed gerufen neue G e s c h w i n d i g k e i t : 5 m/ s ’’’ Möchte man eine Klasse Car implementieren, ist es sinnvoll, sie von der Klasse Vehicle erben zu lassen. Die Klasse Car sieht dann folgendermaßen aus, wobei irrelevante Teile des Codes der Übersicht halber weggelassen wurden. Klasse - Car 1 import v e h i c l e 2 3 4 5 6 7 8 9 10 11 12 c l a s s Car ( v e h i c l e . V e h i c l e ) : def __init__ ( s e l f , m a n u f a c t u r e r , maxSpeed , f u e l S o r t , maxTankLevel ) : v e h i c l e . V e h i c l e . __init__ ( s e l f , m a n u f a c t u r e r , maxSpeed ) self . _fuelSort = fuelSort ’ ’ ’ M a x i m a l e r T a n k f u e l l s t a n d und a k t u e l l e r F u e l l s t a n d i n l. ’’’ s e l f . _maxTankLevel = maxTankLevel s e l f . _currentTankLevel = 0 ... An dieser Stelle sei kurz angemerkt, dass in Python die Mehrfachvererbung zwar möglich, aber recht kompliziert ist. Außerdem wird im Allgemeinen davon abgeraten sie zu benutzen. Daher wird an dieser Stelle auf diese nicht weiter eingegangen. 11 2 Konzepte und Struktur 2.3.2 Magic Methods und Magic Attributes In Python gibt es einige sogenannte Magic Methods beziehungsweise Magic Attributes, die Klassen besondere Fähigkeiten verleihen. Diese Methoden beginnen und enden jeweils mit __. Sie werden als "magisch" bezeichnet, weil sie üblicherweise nicht direkt mit ihrem Namen, sondern implizit im Hintergrund aufgerufen werden. Es handelt sich um einen festen Satz von Magic Methods beziehungsweise Magic Attributes. Die __init__Methode ist eine dieser Methoden. Sie wird implizit beim Verwenden eines Konstruktors ausgeführt. Solche Methoden können beispielsweise benutzt werden, um Operatorüberladungen zu ermöglichen, also das Verwenden desselben Operators auf Instanzen verschiedener Datentypen. Hierzu muss eine Klasse bloß die zum Operator gehörige Methode implementieren. Dies hat man auch schon anhand des Operators + gesehen, denn man kann mit ihm nicht bloß Zahlen addieren, sondern auch Listen oder Strings konkatenieren, weil die entsprechenden Klassen die Methode __add__(self, other) implementieren. Ein Beispiel für ein Magic Attribute wird im Kontext der Annotations (s. Abschnitt 2.6.1) gegeben. 2.4 Exceptionhandling In Python wird das Konzept des Exceptionhandlings ähnlich umgesetzt wie in Java. Es gibt eine gemeinsame Basisklasse, BaseException, von der alle eingebauten Exceptions erben und von der auch alle selbst definierten Exceptions am Ende der Vererbungshierarchie erben sollten. Werfen kann man eine Exception mithilfe des Schlüsselwortes raise. Betrachte hierfür nochmal die Beispielklasse Vehicle. Dort wird im setter des Attributes currentSpeed zunächst überprüft, ob die neue Geschwindigkeit nicht eventuell dazu führt, dass das Vehikel die maximale Geschwindigkeit überschreitet. Ist das nicht der Fall, so wird die neue Geschwindigkeit übernommen. Ist die Geschwindigkeit jedoch zu hoch, so wird hier noch nichts unternommen. Dies wäre also eine geeignete Stelle, um eine Exeption zu werfen. Um das Beispiel kurz zu halten, wird hier keine eigene Exception definiert, sondern die bereits implementierte Exception RuntimeError verwendet. Exception werfen 1 2 3 4 5 6 7 8 9 class Vehicle : ... def s e t C u r r e n t S p e e d ( s e l f , newSpeed ) : i f ( newSpeed <= s e l f . _maxSpeed ) : s e l f . _ c u r r e n t S p e e d = newSpeed else : e r r o r M s g = ( " Die neue G e s c h w i n d i g k e i t i s t zu hoch . " ) raise RuntimeError ( errorMsg ) ... 12 2 Konzepte und Struktur Anschließend muss man nun noch das Abfangen der Exception in unserem Testprogramm von vorhin realisieren. Hierfür wird die try-except- beziehungsweise try-exceptelse-finally-Anweisung benutzt, sodass das Testprogramm wie folgt aussieht und sich die angegeben Ausgabe ergibt. Exception fangen 1 import v e h i c l e 2 3 4 5 6 7 8 9 10 11 12 v = v e h i c l e . V e h i c l e ( " Audi " , 1 4 ) try : v . a c c e l e r a t e (5 , 5) except R u n t i m e E r r o r a s e : print ( " Fehlermeldung : " , e . args [ 0 ] ) else : " w i r d a u s g e f u e h r t , wenn k e i n e E x c e p t i o n g e w o r f e n w i r d " finally : " w i r d immer a l s l e t z t e s a u s g e f u e h r t , u n a b h a e n g i g davon , ob \ e i n e E x c e p t i o n g e w o r f e n wurde o d e r n i c h t " 13 14 15 16 17 ’’’ Ausgabe a u f d e r K o n s o l e b e i A u s f u e h r u n g d i e s e s Moduls : F e h l e r m e l d u n g : Die neue G e s c h w i n d i g k e i t i s t zu hoch . ’’’ Wie erwartet, wird nun beim Ausführen des obigen Testprogramms die RuntimeErrorException vom setter geworfen und vom Testprogramm abgefangen. Dem Benutzer wird somit mitgeteilt, dass das Vehikel nicht so schnell fahren kann. Neben dem tryBereich, in dem der Code steht, dessen Ausführung zum Auftreten einer Exception führen kann, und dem except-Bereich, in dem der Code zur Fehlerbehandlung steht, sind noch zwei weitere Punkte aufgeführt. Diese sind beide optional und bieten die im Code beschriebenen Möglichkeiten. 2.5 Automatisiertes Testen Python wird häufig für das Test-Driven Devolpment (deut. testgetriebene Entwicklung, auch TDD) benutzt. Hierfür stellt die Standardbibliothek zwei Module zur Verfügung. Diese sind doctest und unittest. Mithilfe von doctest können innerhalb von Docstrings Testfälle definiert werden, während unittest die Funktionalitäten des JavaModuls JUnit implementiert [2, S. 718]. 13 2 Konzepte und Struktur 2.5.1 Modul doctest Man betrachte das Beispiel zum Berechnen der Fibonacci-Zahlen vom Anfang und ergänze den Code nun um einen Docstring mit Testfällen. Fibonacci - doctest 1 import d o c t e s t 2 3 4 def f i b ( n ) : ’ ’ ’ B e r e c h n e t d i e n−t e F i b o n a c c i Z a h l . 5 6 7 8 9 10 11 >>> f i b ( 4 ) 5 >>> f i b ( 1 5 ) 987 >>> f i b ( 2 5 ) 121393 12 13 Es muss e i n e Z a h l >= 0 u e b e r g e b e n werden . 14 15 16 17 18 19 >>> f i b ( −2) T r a c e b a c k ( most r e c e n t c a l l l a s t ) : ... ValueError : Keine n e g a t i v e n Zahlen . ’’’ 20 21 22 23 24 25 26 i f ( n == 0 ) : return 1 e l i f ( n == 1 ) : return 1 else : r e t u r n f i b ( n−1) + f i b ( n−2) 27 28 29 i f (__name__ == "__main__" ) : d o c t e s t . testmod ( ) Die einzelnen Testfälle haben die Form, die sie beim direkten Aufrufen der Funktion in der interaktiven Umgebung hätten. Für Testfälle, die Fehler erzeugen, muss nicht der gesamte Stacktrace notiert werden, es reichen stattdessen auch "..." aus. Hier werden die Testfälle mit doctest.testmod() aufgrund der if-Bedingung nur dann ausgeführt, wenn dieses Modul direkt ausgeführt wird, also nicht, wenn das Modul per import von anderen Modulen eingebunden wird. Führt man das Modul aus, erhält man folgende Meldung. 14 2 Konzepte und Struktur Fibonacci - doctest-Testergebnisse 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ’’’ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ F i l e " f i b _ d o c t e s t . py " , l i n e 1 5 , i n __main__ . f i b F a i l e d example : f i b ( −2) Expected : T r a c e b a c k ( most r e c e n t c a l l l a s t ) : ... ValueError : Keine n e g a t i v e n Zahlen . Got : T r a c e b a c k ( most r e c e n t c a l l l a s t ) : ... R u n t i m e E r r o r : maximum r e c u r s i o n d e p t h e x c e e d e d i n c o m p a r i s o n ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 1 i t e m s had f a i l u r e s : 1 of 4 i n __main__ . f i b ∗∗∗ T e s t F a i l e d ∗∗∗ 1 f a i l u r e s . ’’’ Erweitert man die Fibonacci-Funktion nun um eine Überprüfung, ob n kleiner als Null ist und wirft in dem Fall die Exception ValueError mit der Nachricht "Keine negativen Zahlen.", so erhält man bei Ausführung keine Fehlermeldung mehr. Man sieht gar nichts, da im Allgemeinen nur fehlgeschlagene Tests angezeigt werden (führt man das Modul mit der Option -v aus, so werden auch Meldungen zu erfolgreichen Testfällen ausgegeben). Da Docstrings der Dokumentation von Programmcode dienen, sollten in diesen allerdings nur kleine und erklärende Beispiele vorkommen, damit sie auch für die Dokumentation zu gebrauchen sind. Normalerweise eignen sich Docstring-Tests jedoch nicht zum generellen Testen eines Moduls. 2.5.2 Modul unittest Im Gegensatz zum doctest-Verfahren werden hier die Testfälle in einem eigenen Modul, also getrennt von der Programmlogik, definiert, sodass die Dokumentation und der Programmcode sauber bleiben. Hierfür muss eine Klasse erstellt werden, die von der Klasse unittest.TestCase erbt. In dieser werden entsprechende Testmethoden definiert. Üblicherweise testet eine Testklasse nicht mehr als ein Modul. Ein unittest-Modul, das beinahe die gleichen Tests durchführt wie soeben die Docstring-Tests, sieht dann wie folgt aus. 15 2 Konzepte und Struktur Fibonacci - unittest 1 import u n i t t e s t , f i b 2 3 c l a s s FibTest ( u n i t t e s t . TestCase ) : 4 5 6 7 8 def t e s t C a l c u l a t i o n ( s e l f self . assertEqual ( fib . self . assertEqual ( fib . self . assertEqual ( fib . ): f i b ( 4 ) , 6) f i b ( 1 5 ) , 987) f i b ( 2 5 ) , 121393) 9 10 11 def t e s t E x c e p t i o n ( s e l f ) : s e l f . a s s e r t R a i s e s ( V a l u e E r r o r , f i b . f i b , −2) 12 13 14 i f (__name__ == "__main__" ) : u n i t t e s t . main ( ) Beim Testen der vierten Fibonacci-Zahl ist leider ein Fehler unterlaufen und Eingaben kleiner als Null werden noch nicht behandelt, sodass das Ausführen zu Folgendem führt. Fibonacci - unittest-Testergebnisse 1 2 3 4 5 6 7 8 ’’’ FE ================================================================ ERROR : t e s t E x c e p t i o n ( __main__ . F i b T e s t ) −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− T r a c e b a c k ( most r e c e n t c a l l l a s t ) : ... R u n t i m e E r r o r : maximum r e c u r s i o n d e p t h e x c e e d e d i n c o m p a r i s o n 9 10 11 12 13 14 15 16 ================================================================ FAIL : t e s t C a l c u l a t i o n ( __main__ . F i b T e s t ) −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− T r a c e b a c k ( most r e c e n t c a l l l a s t ) : F i l e " f i b _ u n i t t e s t . py " , l i n e 6 , i n t e s t C a l c u l a t i o n s e l f . a s s e r t E q u a l ( f i b . f i b ( 4 ) , 6) A s s e r t i o n E r r o r : 5 != 6 17 18 19 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Ran 2 t e s t s i n 0 . 0 1 5 s 20 21 22 FAILED ( f a i l u r e s =1, e r r o r s =1) ’’’ 16 2 Konzepte und Struktur Behebt man nun den fehlerhaften Test und erweitert das Modul fib, um die Überprüfung auf Eingaben kleiner als Null, so laufen die Tests erfolgreich durch und es ergibt sich folgende Ausgabe. Fibonacci - unittest-Testergebnisse (ohne Fehler) 1 2 3 4 ’’’ .. −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Ran 2 t e s t s i n 0 . 0 8 4 s 5 6 7 OK ’’’ Muss man komplexere Module testen, zum Beispiel eine Klasse, so ist es eventuell nötig, gewisse Initialisierungen zu machen, bevor die eigentlichen Tests durchlaufen können. Auch kann es notwendig sein, nach Ablauf aller Tests abschließenden Code zu schreiben. Hierfür gibt es, wie aus Java bekannt, die Methoden setUp() und tearDown(), die ganz zu Beginn beziehungsweise ganz am Schluss ausgeführt werden. Sollte es innerhalb dieser Methoden dazu kommen, dass eine Exception ausgelöst wird, so wird der Test abgebrochen und dies als Fehler im Testbericht eingetragen. Außerdem gibt es noch weitaus mehr assert-Anweisungen als hier vorgestellt, um Testergebnisse zu überprüfen. 2.6 Besonderheiten 2.6.1 Annotationen Sogenannte Annotations sind Anmerkungen, die bei einer Funktionsdefinition jedem Parameter und dem Rückgabewert hinzugefügt werden können. Syntaktisch sieht dies für das Fibonacci-Beispiel von vorhin folgendermaßen aus. Hier wurde der Parameter n und der return-Wert um eine Typ-Annotation erweitert. Annotations - Syntax 1 2 3 4 5 6 7 def f i b ( n : i n t ) −> i n t : i f ( n == 0 ) : return 1 e l i f ( n == 1 ) : return 1 else : r e t u r n f i b ( n−1) + f i b ( n−2) Eine solche Annotation darf ein beliebiger Python-Ausdruck sein und steht zur Laufzeit in dem Magic-Attribut __annotations__ des Funktionsobjektes fib zur Verfügung. 17 2 Konzepte und Struktur Hierbei handelt es sich um ein Dictionary, wobei ein Parametername ein Schlüssel und die dazugehörige Annotation der jeweilige Wert ist. Das __annotations__-Dictionary zu obigem Beispiel sähe also wie folgt aus. Annotation-Dictionary 1 2 3 4 5 f i b . __annotations__ = { "n" : int , " return " : int } Somit kann der Programmierer beim Definieren einer Funktion Informationen hinterlegen, die er zur Laufzeit für seine Zwecke benutzen möchte. Versieht man die Parameter wie hier mit Typ-Annotationen, so könnte man zur Laufzeit vor der Ausführung des Funktionscodes eine Typprüfung durchführen. Somit hätte man die Möglichkeit die Funktionsausführung vorzeitig mit einer TypeError-Exception abzubrechen, falls ein Parameter Instanz eines nicht passenden Datentyps ist. Außerdem ist es für andere Programmierer beim Lesen des Codes schneller ersichtlich, wofür gewisse Parameter gedacht sind oder welchen Typ sie haben sollen. Annotations ändern nichts an der Funktionslogik, greifen also nicht in die Ausführung der Funktion ein, womit die Annotations in Python zum Programmierparadigma der aspektorientierten Programmierung zählen. 18 3 Technische Unterstützung 3.1 Compiler, Interpreter und Entwicklungsumgebungen Obwohl Python zwar häufig als Skriptsprache verwendet wird und viele Sprachelemente heutiger Skriptsprachen implementiert, handelt es sich dennoch um eine interpretierte Programmiersprache. Wie auch bei Java, erzeugt ein Compiler zunächst aus dem QuellCode den sogenannten Byte-Code, der dann in der virtuellen Maschine, dem PythonInterpreter, ausgeführt werden kann. Für Python gibt es zahlreiche Entwicklungsumgebungen. Eine davon ist IDLE (Integrated Development Environment, s. http://www.python.org/idle). Sie bietet sowohl unter Windows als auch unter Unix-Systemen eine grafische Benutzeroberfläche mit einer Shell und einem hauseigenen Editor. Sie ist vollständig in Python implementiert. IDLE ist in der Lage Code-Schlüsselwörter hervorzuheben (Syntax-Highlighting) und unterstützt die automatische Codevervollständigung. Neben IDLE gibt es beispielsweise noch das Eclipse-Plugin PyDev (s. http://pydev.org). Mit diesem kann man prinzipiell genau so wie man es bei Java-Projekten gewohnt ist, Python-Applikationen schreiben. Es wird Syntax-Highlighting und Codevervollständigung unterstützt und, wie in IDLE, lässt sich ein Modul ausführen, dessen eventuelle Ausgabe in der programmeigenen Konsole beziehungsweise Shell angezeigt wird. 3.2 Werkzeuge und Bibliotheken 3.2.1 Interaktive Umgebung Der Python-Interpreter kann auch ohne Argumente, also ohne die Angabe einer PythonQuelltext-Datei, gestartet werden. Tut man dies, so gelangt man in den interaktiven Modus (auch interaktive Umgebung). In diesem ist es möglich beliebigen PythonCode zu schreiben und sich Ergebnisse dieser Eingaben direkt anzuschauen (vgl. GHCI für Haskell). Dies kann man nutzen, um kleine Beispiele, Ideen oder Konzepte schnell zu testen. Es ist jedoch unkomfortabel und ungeeignet den interaktiven Modus zum Schreiben richtiger Programme zu nutzen. Im interaktiven Modus wird nämlich kein Syntax-Highlighting unterstützt. Außerdem ist es nicht möglich, einzelne Zeilen des eingegeben Codes zu bearbeiten, sondern es müsste der gesamte Code noch einmal eingegeben werden. 19 3 Technische Unterstützung 3.2.2 Standardbibliothek Python macht es dank seiner umfangreichen Standardbibliothek möglich, mit wenig Aufwand recht effiziente umfangreiche Programme in unterschiedlichsten Bereichen zu schreiben, ohne übermäßig viel Quelltext zu erzeugen. Die Standardbibliothek bietet neben den üblichen Bibliotheken für Mathematik (Modul math) oder Datum und Zeit (Modul time) auch Bibliotheken zum Programmieren von parallelen Programmen (Module _thread und threading), für die Netzwerkkommunikation (Modul socket), die Datenspeicherung (Module gzip und xml) sowie für das Debugging (Module inspect und logging). Auch eine Bibliothek, die als Schnittstelle zum Betriebssystem dient, wird angeboten (Module os, shutil, platform und sys). 3.3 Portabilität Da Python wie Java zur Ausführung einen Interpreter, also eine virtuelle Maschine, benötigt, ist Python sehr portabel. Man kann hier sogar von Plattformunabhängigkeit sprechen, da es mindestens für die drei bekanntesten Betriebssysteme (Windows, Mac, Linux) Interpreter gibt. Es existieren verschiedene Python-Interpreter, wobei es sich bei CPython (auch cPython, s. http://www.python.org) um den standardmäßig installierten und am häufigsten verwendeten handelt. Dieser ist die Referenzimplementation des Python Interpreters, wurde in C geschrieben und wird als Open-Source-Software von der PSF entwickelt [4]. Es existieren noch weitere Implementationen von Python-Interpretern, zu diesen zählt unter anderem PyPy (s. http://pypy.org). 20 4 Zusammenfassung und Fazit Diese Seminararbeit hat einen kleinen Überblick über die Programmiersprache Python gegeben. Hierbei wurden sowohl die Geschichte der Sprache als auch einige ausgewählte Features betrachtet. Zunächst wurden Grundlagen zur Sprache vorgestellt und anschließend einige Konzepte der Sprache behandelt. Die untersuchten Konzepte waren die funktionale und objektorientierte Programmierung in Python. Auch das Exceptionhandling, das automatisierte Testen und Annotationen wurden besprochen. Außerdem wurden Entwicklungsumgebungen und Werkzeuge für Python vorgestellt sowie besondere Eigenschaften der Sprache, wie zum Beispiel ihre Portabilität. Diese Aspekte der Programmiersprache Python zeigen bereits einige Vor- und Nachteile. Einerseits ist an Pythons Syntax zu kritisieren, dass einem als Programmierer vorgegeben wird, wie man seinen Code zu formatieren hat. Dies ist zwar gerade für Anfänger sicherlich sinnvoll und lehrreich, wird jedoch von vielen fortgeschrittenen Programmieren als störend empfunden. Beispielsweise können dadurch Einrückungen, die aufgrund der Optik gemacht werden, nicht vorgenommen werden, da sie zu einem Syntax-Fehler führen. Weiterhin ist es unschön und redundant jeder Methode einer Klasse explizit die Referenz self übergeben zu müssen. Außerdem ist Python nicht für zeitkritische Systeme gedacht, da Programme auf einer virtuellen Maschine ausgeführt werden, was Leistung und Zeit kostet, wie dies von Java bereits bekannt ist. Andererseits bietet Python die Möglichkeit leicht lesbaren und mächtigen Code zu schreiben, der gerade im Vergleich zu anderen Programmiersprachen wie Java deutlich kürzer sein kann, was vor allem an der umfangreichen Standardbibliothek liegt, denn diese spart einem viel Code, den man in anderen Programmiersprachen teilweise selbst schreiben muss. Dies trägt zur großen Flexibilität von Python bei, wodurch Python in vielen Anwendungsgebieten eingesetzt werden kann. Weiterhin ist Python leicht um weitere Bibliotheken (von Drittanbietern oder sogar eigene) erweiterbar, was wiederum zur Flexibilität und Mächtigkeit von Python beiträgt. Aufgrund der einfachen Syntax ist Python leicht zu erlernen, was es, zusammen mit der automatischen Speicherverwaltungen, auch weniger erfahrenen Programmierern möglich macht, schnell komplexe Programme zu schreiben. Auch ist es wegen der Portabilität von Python leicht möglich, plattformunabhängige Applikationen zu schreiben. Schlussendlich ist Python zwar keine so weit verbreitete Programmiersprache wie es Java ist, sie hat aber ihre Anhänger. Meiner Ansicht nach wurde ihr volles Potential sowohl für kommerzielle Zwecke als auch für Open Source Projekte bisher noch nicht erkannt. 21 Literaturverzeichnis [1] Guido van Rossum. Website. Online unter http://de.wikipedia.org/wiki/Guido_ van_Rossum;besucht am 18.01.2013. [2] Johannes Ernesti; Peter Kaiser. Python3 Das umfassende Handbuch. Galileo Press, 2012. 3., aktualisierte und erweiterte Auflage. [3] Duck-Typing. Website. Online unter http://de.wikipedia.org/wiki/ Duck-Typing;besucht am 18.01.2013. [4] CPython. Website. Online unter http://en.wikipedia.org/wiki/CPython und http://www.python.org;besucht am 04.02.2013. [5] Python Dokumentation. Website. 3/;besucht am 02.12.2012. Online unter http://docs.python.org/ [6] Python (Programmiersprache). Website. Online unter http://de.wikipedia.org/ wiki/Python_%28Programmiersprache%29;besucht am 18.01.2013. [7] Vorlesung der Uni Osnabrück. Website. Online unter http://www.vorlesungen. uni-osnabrueck.de/informatik/altprog00/python/node1.html;besucht am 02.12.2012. [8] Die Programmiersprache Python (TU Chemnitz). Website. Online unter http: //www-user.tu-chemnitz.de/~hot/PYTHON/;besucht am 02.12.2012. 22