![]() |
|
||||||||||||||||||
|
|||||||||||||||||||
|
|||||||||||||||||||
|
||
|
TFT Monitor bei
Mercateo kaufen.
Neues Netbook? Ein Preisvergleich lohnt sich. Bei uns finden sie Notebooks, PDAs und Drucker mit Testberichten und Tipps. Diamant Buchhaltungssoftware – transparent und detailliert auch für die Konzernbuchhaltung. Günstige Shareware Programme als direkte Downloads im Software Portal. Bis zu 70% sparen durch Preisvergleich. |
||
|
Stefan Schwarzer
| README |
|
Die dritte Folge des Kurses beschreibt, wie Python mit Programmierfehlern umgeht und führt in die Konzepte des objektorientierten Programmierens ein. |
Im ersten Teil des Python-Kurses (LinuxUser 09/2006, S. 80) haben Sie den interaktiven Interpreter, Datentypen und Ablaufstrukturen kennengelernt. Wie Sie im zweiten Teil (LinuxUser 10/2006, S. 68) gesehen haben, macht Python das Aufteilen von Code in Funktionen, Module und Pakete ganz einfach. Dieser dritte Teil zeigt, wie Python Fehler behandelt und bietet einen Einstieg in die objektorientierte Programmierung.
Haben Sie schon ein Programm zum Bearbeiten von Dateien geschrieben, ist Ihnen möglicherweise aufgefallen, dass Python Fehler beim Öffenen einer Datei nicht besonders freundlich handhabt: Fehlt zum Beispiel das entsprechende File, bricht das Programm beim Aufruf datei = open("nicht_da.txt") mit der folgenden Fehlerausgabe ab:
Traceback (most recent call last):
File "./nicht_da.py", line 4, in ?
datei = open("nicht_da.txt")
IOError: [Errno 2] No such file or directory: 'nicht_da.txt'
Bei der Ausgabe handelt es sich um einen sogenannten Traceback, der zeigt, wo der Fehler auftrat und welche Funktionen beteiligt waren, bevor der Fehler auftrat. Im Beispiel gibt es nur eine Zeile direkt auf Modulebene, deshalb ist von der Aufrufreihenfolge nicht viel zu sehen. Python kennt nun Möglichkeiten zum Abfangen des Fehlers:
try:
datei = open("nicht_da.txt")
except IOError:
print "Fehler beim Öffnen der Datei"
print "Und weiter nach dem Fehler ..."
Dabei ist IOError der Typ der Ausnahme, wie er aus dem Traceback erkennbar ist. Allerdings nutzen die zwei Zeilen bei der Fehlersuche wenig, weil die Angabe der problematischen Datei in der Ausgabe fehlt. Den Dateinamen bekommen Sie durch eine kleine Änderung am Code:
try:
datei = open("nicht_da.txt")
except IOError, io_exception:
print 'Fehler beim Öffnen der Datei "%s"' % io_exception.filename
print "Und weiter nach dem Fehler ..."
Das durch io_exception bezeichnete Objekt ist ein Ausnahme-Objekt, hier vom Typ IOError. IOError-Objekte haben unter anderem ein Attribut filename, das die problematische Datei bezeichnet.
Nach dem Ändern lautet die Ausgabe des obigen Codes:
Fehler beim Öffnen der Datei "nicht_da.txt" Und weiter nach dem Fehler ...
Wie Sie sehen, erscheint zwar der Fehlertext, das Programm geht aber über den Fehler hinweg. Beachten Sie, dass Sie eine Ausnahme nicht direkt beim fehlererzeugenden Code abfangen müssen (Listing 1).
| Listing 1 |
01 import sys
02
03 def log_datei(datei_name):
04 return open(datei_name, 'w')
05
06 def start():
07 # "global" sparsam verwenden
08 global log
09 log = log_datei("prog.log")
10 print "Programm gestartet"
11
12 def main():
13 try:
14 start()
15 except IOError, exc:
16 print exc
17 sys.exit()
18
19 if __name__ == '__main__':
20 main()
|
Existiert die Datei prog.log Datei nicht oder verweigert das Betriebssystem den Zugriff, pflanzt sich der Fehler so lange fort, bis das Try/Except-Konstrukt in der Funktion main ihn abgefängt. Falls keine Fehlerbehandlung für einen bestimmten Ausnahmetyp vohanden ist, beendet sich das Programm mit einem Traceback. Unvorhergesehene Fehler fallen also immer auf.
Wollen Sie mehrere mögliche Ausnahmen behandeln, bietet es sich an, diese mit verschiedenen except-Zweigen abzufangen (Listing 2). Ist die Behandlung für beide Fehlerarten gleich, sollten Sie Redundanz vermeiden, indem Sie beide except-Zweige zusammenfassen.
| Listing 2 |
01 try: 02 tu_was() 03 except IOError, io_exception: 04 # verwende io_exception, um 05 # den Fehler zu behandeln 06 ... 07 except OSError, os_exception: 08 # verwende os_exception, um 09 # den Fehler zu behandeln 10 ... |
try:
tu_was()
except (IOError, OSError):
...
Beim Konstrukt im Listing 3 sind die Klammern zwingend notwendig. Fehlen sie, versteht Python unter IOError, OSError ein Ausnahme-Objekt vom Typ IOError namens OSError, analog zum Beispiel IOError, io_exception weiter oben. Auch hier ist zusätzlich die Angabe eines Namens für ein Ausnahmeobjekt möglich, das Ihnen dann weitere Informationen bereitstellt.
try:
tu_was()
except (IOError, OSError), exception:
...
Das hat jedoch den Nachteil, dass exception je nach aufgetretenem Fehler einen unterschiedlichen Typ und damit verschiedene Attribute hat. Um sich Fallunterscheidungen zu ersparen, sollten Sie getrennte except-Zweige nehmen. Wollen Sie alle Ausnahmen -- egal welche -- abfangen, verwenden Sie except ohne Ausnahmetypen:
try:
tu_was()
except:
...
Diese Möglichkeit spart auf den ersten Blick Schreibarbeit. Sie hat aber ihre Tücken: Damit fallen alle Ausnahmen unter den Tisch, unter anderem KeyboardInterrupt (Drücken von [Strg]+[C]) und SyntaxError (zum Beispiel beim Importieren eines fehlerhaften Moduls). Daher sollten Sie die obige Variante nur auf der obersten Ebene einer Software verwenden, um beispielsweise Traceback-Ausgaben in einem Produktionssystem zu verhindern.
Fangen Sie in einer Try/Except-Anweisung mehrere spezifische Fehler parallel zu einer allgemeinen except-Zeile ab, kommt es auf die richtige Reihenfolge an: Die Zeilen mit den speziellen Exceptions stehen vor dem allgemeinen except (Listing 3). Alle Try/Except-Konstrukte erlauben einen else-Zweig, der zum Zuge kommt, wenn keine Ausnahme ausgelöst auftrat (Listing 4).
| Listing 3 |
01 try: 02 tu_was() 03 except IOError: 04 ... 05 except OSError: 06 ... 07 except: 08 ... |
| Listing 4 |
01 try:
02 datei = open("test.txt")
03 except IOError:
04 print "I/O-Fehler"
05 else:
06 print "alles ok"
|
Manchmal ist es gewünscht, bestimmten Code auszuführen, egal ob ein Fehler auftritt oder nicht. Das betrifft freizugebende Ressourcen wie Dateiobjekte, Sockets oder Datenbankverbindungen. Zu diesem Zweck gibt es das Konstrukt try ... finally: Im Beispiel wird die Datenbankverbindung geschlossen, gleichgültig, ob tu_was eine Ausnahme hervorgerufen hat oder nicht.
conn = hole_verbindung()
try:
tu_was(conn)
finally:
conn.close()
Beachten Sie, dass das Konstrukt Try/Finally nicht das Gleiche macht, wie der else-Zweig in Try/Except/Else. Der finally-Block kommt immer zum Zuge, der else-Block nur, wenn keine Ausnahme auftritt. Bis einschließlich Python 2.4 müssen Sie try ... except und try ... finally verschachteln, wenn Sie beides brauchen (Listing 5). Ab Python 2.5 funktioniert auch die Kombination auf einer Ebene mit derselben Bedeutung [1].
| Listing 5 |
01 try: 02 try: 03 ... 04 except ...: 05 ... 06 finally: 07 ... |
| Listing 6 |
01 try: 02 ... 03 except ...: 04 ... 05 finally: 06 ... |
Stellen Sie sich vor, Sie wollen von bestimmten Bedingungen abhängig machen, ob eine Ausnahme in Ihrer Software als Fehler gilt. Zum Beispiel könnten Sie versuchen, eine -- optionale -- Konfigurationsdatei zu öffnen. Ist sie nicht vorhanden, ignoriert das Programm den Fehler; ist sie aber vorhanden und nicht lesbar, löst der Code die abgefangene Ausnahme erneut aus. Dazu verwenden Sie mit die Anweisung raise (siehe Listing 7).
| Listing 7 |
try:
config_file = open("/etc/myprog/config")
except IOError, io_exception:
if io_exception.errno == 2:
# Datei nicht gefunden: ignorieren
pass
else:
# Ausnahme (erneut) auslösen
raise
|
Mit raise lösen Sie nicht nur die letzte Ausnahme erneut aus; über eine selbstgewählte Ausnahme mit Typ und eventuellen Parametern erzeugt Sie zum Beispiel einheitlich formatierte Fehlermeldungen. So produziert raise ValueError("Zahl %s muss positiv sein" % x) ein ValueError-Objekt mit dem Fehlertext in Klammern. Weiter unten in diesem Artikel erfahren Sie, wie Sie eigene Ausnahmetypen erzeugen.
Beim Schreiben von größeren Programme hilft eine objektorientierte Struktur, da sie den Code von ganz alleine gliedert und so auch anderen das Verständnis für die Funktion erleichtert. Dabei steht nicht im Vordergrund, was einzelne Funktionen mit welchen Daten machen, sondern Dinge (Objekte), ihre Eigenschaften (beschrieben durch Attribute) und ihr Verhalten (beschrieben durch Methoden). Eine Klasse beschreibt eine bestimmte Art von Dingen, ist also allen Dingen dieser Art gemeinsam.
Ein Beispiel veranschaulicht, was diese trockene Theorie bedeutet: Nehmen wir eine Klasse "Person". Im interaktiven Interpreter von Python definieren Sie eine solche Klasse folgendermaßen:
>>> class Person(object): ... pass
Obwohl die Angabe (object) nicht zwingend notwendig ist, empfielt sie sich, um Klassen als Klassen neuerer Art auszuzeichnen. Zum Hintergrund: Zwischen den Python-Versionen 2.1 und 2.2 überarbeiteten die Entwickler die Implementation von Klassen überarbeitet. Alte Klassen dürfen Sie aus Kompatibilitätsgründen nach wie vor mit class Klasse: definieren. Die richtige Schreibweise verlangt aber die Angabe der Klammerblocks.
Sie erzeugen, oder genauer instanzieren, Objekte einer Klasse, indem Sie die Klasse wie eine Funktion verwendet. Mit mike = Person() und willi = Person() erschaffen Sie zwei Person-Instanzen. Durch die Definition ist es kaum möglich, zwei Person-Objekte zu unterscheiden. Das geht nur anhand einer Objektkennung, die Sie mit der Python-Funktion id erhalten:
>>> id(mike) -1213247604 >>> id(willi) -1213256052
Wesentlich nützlicher sind die Person-Objekte, wenn Sie Eigenschaften der entsprechenden Personen darin speichern:
>>> willi = Person() >>> willi.vorname = "Willi" >>> willi.nachname = "Wusel" >>> print willi.vorname Willi
Wie Sie sehen, dürfen Sie in Objekten beliebige Eigenschaften (Attribute) anlegen und abfragen. Das gilt allerdings nicht für Objekte, wie die in Python eingebauten Typen, zum Beispiel list:
>>> L = [] >>> L.neu = 1 Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'list' object has no attribute 'neu'
Objekte können mittels Methoden auch Verhalten implementieren. Zum Beispiel können wir Personen gehen lassen:
>>> class Person(object): ... def gehe(self, tempo=3): ... "Setze Tempo in km/h." ... self.tempo = tempo
Das erste Argument einer Methode bezeichnet das Objekt, für das die Methode aufgerufen wird und heißt konventionsgemäß self. Danach folgen weitere Parameter, analog zu Funktionsdefinitionen (s. zweiter Artikel der Python-Einführung). In der Methode gehe besitzt tempo den Vorgabewert 3. Die Anweisung self.tempo = tempo verknüpft die Instanzvariable tempo des durch self referenzierten Person-Objekts mit dem Wert des Arguments tempo. Nach der Zuweisung bezeichnen also self.tempo und tempo das gleiche Objekt.
Die Methode gehe ist nun wie folgt benutzbar:
>>> willi = Person() >>> willi.vorname = "Willi" >>> print willi.vorname Willi >>> willi.gehe() >>> print willi.tempo 3 >>> willi.gehe(5) >>> print willi.tempo 5 >>> mike = Person() >>> # mike.tempo ist undefiniert ... print mike.tempo Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'Person' object has no attribute 'tempo' >>> mike.gehe(4) >>> print mike.tempo 4 >>> # unabhängig von mike.tempo ... print willi.tempo 5
Wie das obige Beispiel zeigt, erzeugt der Zugriff auf ein noch nicht definiertes Attribut einen AttributeError. Um diesen zu vermeiden, setzen Sie am besten einen Wert in der so genannten Konstruktormethode __init__, die Python beim Erzeugen eines Objekts ausführt:
>>> class Person(object): ... def __init__(self): ... # Person steht ... self.gehe(0) ... def gehe(self, tempo=3): ... "Setze Tempo in km/h." ... self.tempo = tempo ... >>> mike = Person() >>> print mike.tempo 0
Auch die Definition der __init__-Methode erlaubt Argumente:
>>> class Person(object): ... def __init__(self, vorname, nachname): ... self.vorname = vorname ... self.nachname = nachname ... self.gehe(0)
Im Gegensatz zu C++ und Java gibt es höchstens einen Konstruktor; das "Überladen" der Konstruktormethode durch unterschiedliche Parameterlisten ist nicht möglich. Programmierer umgehen diese Einschränkung in Python üblicherweise auf eine der Arten, die der Kasten "Mehrfache Methoden-Schnittstellen" beschreibt.
| Mehrfache Methoden-Schnittstellen |
|
In diesem Beispiel akzeptiert der Konstruktor einer Klasse None-VorgabewerteDas Beispiel verwendet im Konstruktor sowohl für das Dateiobjekt als auch für den Dateinamen als Vorgabe den speziellen Wert class LogFile(object):
def __init__(self, file_object=None, file_name=None):
# prüfe, ob genau ein Parameter ungleich None ist
if [file_object, file_name].count(None) != 1:
# Fehlerbehandlung
...
if file_name is not None:
self.file_object = open(file_name, 'w')
else:
self.file_object = file_object
Um nun die Klasse richtig zu instanzieren, ist es notwendig, im Aufruf den Namen des Arguments zu verwenden. Der Aufruf class _MissingParameter(object):
pass
class LogFile(object):
def __init__(self,
file_object=_MissingParameter,
file_name=_MissingParameter):
...
Der führende Unterstrich in EigenschaftenHierbei findet eine Prüfung statt, ob ein übergebenes Argument zum Beispiel ein bestimmtes Attribut besitzt oder ob eine bestimmte Operation möglich ist. Im Beispiel arbeitet die Funktion class LogFile(object):
def __init__(self, file_name_or_object):
try:
self.file_object = open(file_name_or_object, 'w')
except TypeError:
self.file_object = file_name_or_object
Ein Vorteil dieses Verfahrens ist die Kompaktheit des Codes. Der Nachteil liegt, dass Sie schon beim Programmieren die Art der auszulösenden Ausnahme kennen müssen (hier Verändern Sie die Schnittstelle im Konstruktor entsprechend, tritt das Problem deutlicher zutage. In diesem Fall hängt die Hilfsfunktion einen festen Pfadbestandteil an den Dateinamen an: def _in_var_log(file_name):
return "/var/log/" + file_name
class LogFile(object):
def __init__(self, file_name_or_object):
try:
self.file_object = open(_in_var_log(file_name_or_object), 'w')
except TypeError:
self.file_object = file_name_or_object
Der Explizite TypprüfungWer bereits erste Erfahrungen mit C++ oder Java gesammelt hat, ist eventuell versucht, das implizite Unterscheiden von Typen überladener Konstruktoren in Python nachzubilden. Im import types
class LogFile(object):
def __init__(self, file_name_or_object):
if isinstance(file_name_or_object, types.FileType):
self.file_object = file_name_or_object
else:
self.file_object = open(file_name_or_object, 'w')
Diese Variante verträgt sich nur sehr schlecht mit Python-Prinzipien. Zum Beispiel versagt sie für so genannte dateiartige Objekte (z. B. Objekte der Vermeidung mehrfacher Methoden-SchnittstellenNormalerweise ist der Zweck mehrfacher Konstruktorschnittstellen, ein Objekt auf verschiedenen Wegen zu initialisieren. Das erreichen Sie aber auch durch zusätzliche Initialisierungsmethoden: class LogFile(object):
def init_from_file_object(self, file_object):
self.file_object = file_object
def init_from_file_name(self, file_name):
self.file_object = open(file_name, 'w')
Dieses Beispiel benötigt keine Methode log_file = LogFile()
log_file.init_from_file_name("log_datei_name")
Dieser Ansatz ist sehr geradlinig und deshalb leicht verständlich. Ein möglicher Nachteil dieser Programmierschnittstelle liegt darin, dass beim Erzeugen der Instanz zunächst ein halbfertiges Objekt entsteht, was sich in nebenläufigen (multi-threaded) Programmen manchmal als ein Problem erweist. Das beschriebene Verfahren ist, entsprechend abgewandelt, natürlich auch bei anderen Methoden als Konstruktoren möglich. |
Im Gegensatz zu manchen anderen Sprachen sind für Python Methoden nichts anderes als ausführbare Attribute (Listing 8). Der Begriff "bound" bedeutet, dass die Methode an ein bestimmtes Objekt (hier mike) gebunden ist; "unbound" bezieht sich auf die der Klasse zugeordnete Methodendefinition.
| Listing 8 |
01 >>> print mike.gehe 02 <bound method Person.gehe of <__main__.Person object at 0xb7c8a52c>> 03 >>> print callable(mike.gehe) 04 True 05 >>> print Person.gehe 06 <unbound method Person.gehe> |
Durch die sogenannte Vererben oder Ableiten greift eine Klasse auf den Code aus anderen Klassen zu. Im Listing 9 sehen Sie ein Beispiel, das die zuletzt angegebene Person-Klasse verwendet. Die Zeilennummern gehören nicht zum Programm, sondern dienen nur der Referenz im Text.
| Listing 9 |
01 class LinuxUserAutor(Person):
02 """Ein Autor eines oder mehrerer Artikel im LinuxUser."""
03
04 def __init__(self, vorname, nachname):
05 """Initialisiere ein Autor-Objekt."""
06 Person.__init__(self, vorname, nachname)
07 self.artikel = []
08
09 def schreibe(self, artikel_titel):
10 """
11 Füge den bisher geschriebenen Artikeln einen mit dem
12 Titel artikel_titel hinzu.
13 """
14 self.artikel.append(artikel_titel)
15
16 willi = LinuxUserAutor("Willi", "Wusel")
17 willi.schreibe("Seltsame Programmbeispiele")
18 print willi.artikel # ["Seltsame Programmbeispiele"]
19 willi.gehe(5)
20 print willi.tempo # Ergebnis: 5
|
Die class-Anweisung in Zeile 1 enthält in den Klammern den Namen der Klasse (Basisklasse), von der die neue Klasse erbt. Für Programmierer anderer objektorientierter Sprachen: Python unterstützt auch Mehrfachvererbung; Informationen zur Suchreihenfolge bei komplexen Hierarchien finden Sie in [3]. Damit erhält die neue Klasse alle Attribute der Basisklasse.
Die Anweisung in Zeile 6 ruft den Konstruktor der Basisklasse auf, also Person.__init__, bevor in Zeile 7 das neue Autor-Objekt mit eine Liste der veröffentlichten Artikel erhält. Durch diese Zuweisung im Konstruktor bekommt jeder Autor seine eigene Artikelliste. Die Codezeilen 9 bis 14 definieren eine neue Methode schreibe, die der Artikelliste einen neuen Artikel (bezeichnet durch dessen Namen) hinzufügt.
Die Zeilen 16 bis 20 zeigen die Verwendung der Klasse LinuxUserAutor. In Zeile 16 nimmt der Konstruktor die gleichen Parameter wie der der Person-Klasse, was der Definition in Zeile 4 zu entnehmen ist. Der Autor darf nun Artikel schreiben (Zeilen 17 und 18). Zusätzlich hat ein Autor durch die Vererbung alle Eigenschaften der Person-Klasse, so dass in den Zeilen 19 und 20 die Verwendung der Methode Person.gehe möglich ist. Abbildung ((1)) zeigt die Vererbungshierarchie von Person und LinuxUserAutor als UML-Klassendiagramm.
Person und LinuxUserAutor in einem UML-Klassendiagramm.
Die Vererbung sollten Sie nur verwenden, wenn die abgeleitete Klasse vom Inhalt verwandt mit der Basisklasse ist. Ein Autor ist bspielsweise eine Art Person. Gibt es eine solche Beziehung nicht, benutzen Sie die Aggregation. Hierbei enthält eine Klasse Objekte einer anderen Klasse als Daten statt von der Klasse zu erben. Dieser Code mit Vererbung ist nicht empfehlenswert:
class Prozessor(object):
...
class Computer(Prozessor):
...
Verwenden Sie statt dessen lieber diesen Ansatz mit Aggregation:
class Prozessor(object):
...
class Computer(object):
def __init__(self):
self.prozessor = Prozessor()
...
Nun ist die Zeit reif für einen Nachtrag zum behandeln von Ausnahmen: Neben den in Python eingebauten Ausnahmentypen [5] eröffnet die Sprache die Möglichkeit, eigene Ausnahmen zu programmieren. Es empfiehlt sich, diese Möglichkeit nach Kräften zu nutzen. Dazu leiten Sie von der eingebauten Klasse Exception ab (siehe Listing 10).
| Listing 10 |
01 >>> class FalscheRichtung(Exception):
02 ... pass
03 ...
04 >>> class Person(object):
05 ... def gehe(self, tempo=3):
06 ... """Setze Tempo in km/h."""
07 ... if tempo < 0:
08 ... raise FalscheRichtung("rueckwaerts gehen ist gefaehrlich")
09 ... self.tempo = tempo
10 ... print "Tempo ist jetzt", tempo, "km/h."
11 ...
12 >>> willi = Person()
13 >>> willi.gehe(3)
14 Tempo ist jetzt 3 km/h.
15 >>> willi.gehe(-1)
16 Traceback (most recent call last):
17 File "<stdin>", line 1, in ?
18 File "<stdin>", line 4, in gehe
19 __main__.FalscheRichtung: rueckwaerts gehen ist gefaehrlich
|
Der Einsatz eigener Ausnahmetypen hat den Vorteil, dass beim gezielten Prüfen auf bestimmte Ausnahmetypen in Try/Except-Anweisungen entsprechend auf das Ergebnis reagieren. Sie testen also nicht auf einen ValueError, der je nach Test viele Ursachen haben könnte, sondern auf FalscheRichtung, was Ihnen anzeigt, dass eine Person versucht hat, Ihren Programmcode falsch aufzurufen.
Um eine möglichst genaue Kontrolle über die auftretenden Fehler zu haben, ist es üblich, verzweigte Vererbungshierarchien aufzubauen. Schreiben Sie beisielsweise ein Modul, könnte an dessen Anfang Folgendes stehen:
class Fehler(Exception): pass class Dateifehler(Fehler): pass class Formatfehler(Fehler): pass
Damit können Sie durch
import modul
try:
modul.tu_was()
except modul.Fehler:
fehlerbehandlung()
sowohl Dateifehler als auch Formatfehler auf einmal abfangen. Alternativ können Sie beide Fehler getrennt behandeln:
import modul
try:
modul.tu_was()
except modul.Dateifehler:
behandle_dateifehler()
except modul.Formatfehler:
behandle_formatfehler()
Sie könnten auf die Idee kommen, Fehler nur anhand der Fehlertexte zu unterscheiden statt über ihre Klassen. Tun Sie das, aus mehreren Gründen, bitte nicht:
Machen Sie Ihre Programme daher robuster, indem Sie Ausnahmen immer nur anhand ihrer Klassen unterscheiden. Definieren Sie im Zweifelsfall eine Vererbungshierarchie von Ausnahmetypen.
| Infos |
|
[1] Neue Try/Except/Finally-Syntax: http://docs.python.org/dev/whatsnew/pep-341.html
[2] StringIO-Modul: http://docs.python.org/lib/module-StringIO.html
[3] Suchreihenfolge in komplexen Vererbungshierarchien: http://www.python.org/download/releases/2.2/descrintro/#mro [4] Unified Modeling Language: http://de.wikipedia.org/wiki/Unified_Modeling_Language [5] Eingebaute Ausnahmen: http://docs.python.org/lib/module-exceptions.html |
| Der Autor |
|
Dr.-Ing. Stefan Schwarzer verwendet Python seit sieben Jahren und hat bei Addison-Wesley "Workshop Python" veröffentlicht. Er ist selbstständiger Softwareentwickler und ist unter sschwarzer@sschwarzer.net zu erreichen. |
Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links" nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedruckten Fassung entsprechen.
Druckerfreundliche Version |
Feedback zu dieser Seite
|
Datenschutz |
© 2010 Linux New Media AG
[Linux-Magazin]
[LinuxUser]
[EasyLinux]
[Linux-Community]
[Ubuntu User]
[Linux Technical Review]
[Linux Magazine]
[Linux Pro Magazine]
[Ubuntu User]
[EasyLinux Poland]
[Linux Magazine Poland]
[Linux Magazine Brasil]
[EasyLinux Brasil]
[Linux Magazine Spain]