Was gibt es Neues in Python 2.0

Autor:

A.M. Kuchling und Moshe Zadka

Einleitung

Eine neue Version von Python, Version 2.0, wurde am 16. Oktober 2000 veröffentlicht. Dieser Artikel behandelt die aufregenden neuen Funktionen in 2.0, hebt einige andere nützliche Änderungen hervor und weist auf einige inkompatible Änderungen hin, die möglicherweise eine Umschreibung von Code erfordern.

Die Entwicklung von Python stoppt nie vollständig zwischen den Veröffentlichungen, und ein stetiger Strom von Fehlerbehebungen und Verbesserungen wird immer eingereicht. Eine Menge kleinerer Korrekturen, einige Optimierungen, zusätzliche Docstrings und bessere Fehlermeldungen sind in 2.0 eingeflossen; sie alle aufzulisten wäre unmöglich, aber sie sind sicherlich bedeutsam. Konsultieren Sie die öffentlich zugänglichen CVS-Logs, wenn Sie die vollständige Liste sehen möchten. Dieser Fortschritt ist auf die fünf Entwickler von PythonLabs zurückzuführen, die jetzt bezahlt werden, um ihre Tage mit der Behebung von Fehlern zu verbringen, und auch auf die verbesserte Kommunikation, die sich aus dem Umzug nach SourceForge ergibt.

Was ist mit Python 1.6?

Python 1.6 kann als die "Contractual Obligations Python"-Veröffentlichung betrachtet werden. Nachdem das Kernentwicklungsteam CNRI im Mai 2000 verlassen hatte, bat CNRI darum, eine 1.6-Veröffentlichung zu erstellen, die die gesamte Arbeit an Python enthielt, die bei CNRI durchgeführt worden war. Python 1.6 repräsentiert daher den Zustand des CVS-Trees ab Mai 2000, wobei die signifikanteste neue Funktion die Unicode-Unterstützung ist. Die Entwicklung ging natürlich auch nach Mai weiter, sodass der 1.6-Tree einige Korrekturen erhielt, um sicherzustellen, dass er vorwärtskompatibel mit Python 2.0 ist. 1.6 ist daher Teil der Evolution von Python und keine Seitenlinie.

Sollten Sie also großes Interesse an Python 1.6 haben? Wahrscheinlich nicht. Die Veröffentlichungen 1.6final und 2.0beta1 erfolgten am selben Tag (5. September 2000), wobei der Plan war, Python 2.0 innerhalb eines Monats oder so zu finalisieren. Wenn Sie Anwendungen zu warten haben, scheint es wenig Sinn zu haben, Dinge durch den Umzug nach 1.6 zu beschädigen, sie zu reparieren und dann innerhalb eines Monats eine weitere Runde von Beschädigungen beim Umzug nach 2.0 zu erleben; es ist besser, direkt zu 2.0 zu gehen. Die meisten der wirklich interessanten Funktionen, die in diesem Dokument beschrieben werden, sind nur in 2.0 enthalten, da zwischen Mai und September viel Arbeit geleistet wurde.

Neuer Entwicklungsprozess

Die wichtigste Änderung in Python 2.0 betrifft möglicherweise nicht den Code selbst, sondern die Art und Weise, wie Python entwickelt wird: Im Mai 2000 begannen die Python-Entwickler, die von SourceForge bereitgestellten Tools zur Speicherung von Quellcode, zur Verfolgung von Fehlerberichten und zur Verwaltung der Warteschlange von Patch-Einreichungen zu nutzen. Um Fehler zu melden oder Patches für Python 2.0 einzureichen, verwenden Sie die Bug-Tracking- und Patch-Manager-Tools, die auf der Projektseite von Python unter https://sourceforge.net/projects/python/ verfügbar sind.

Der wichtigste der jetzt bei SourceForge gehosteten Dienste ist der Python CVS-Tree, das versionierte Repository, das den Quellcode für Python enthält. Zuvor hatten etwa 7 Personen Schreibzugriff auf den CVS-Tree, und alle Patches mussten von einer Person auf dieser kurzen Liste inspiziert und eingecheckt werden. Offensichtlich war dies nicht sehr skalierbar. Durch die Verlagerung des CVS-Trees nach SourceForge wurde es möglich, mehr Personen Schreibzugriff zu gewähren; im September 2000 konnten 27 Personen Änderungen einchecken, eine Vervierfachung. Dies ermöglicht groß angelegte Änderungen, die nicht versucht würden, wenn sie durch die kleine Gruppe von Kernentwicklern gefiltert werden müssten. Zum Beispiel nahm sich Peter Schneider-Kamp eines Tages vor, die K&R C-Kompatibilität aufzugeben und den C-Quellcode für Python in ANSI C zu konvertieren. Nach der Genehmigung auf der python-dev Mailingliste startete er eine Flut von Check-ins, die etwa eine Woche dauerten, andere Entwickler schlossen sich an, um zu helfen, und die Arbeit war getan. Wenn es nur 5 Personen mit Schreibzugriff gegeben hätte, wäre diese Aufgabe wahrscheinlich als "nett, aber nicht die Mühe wert" angesehen worden und wäre nie erledigt worden.

Die Umstellung auf die Nutzung der Dienste von SourceForge hat zu einer bemerkenswerten Beschleunigung der Entwicklung geführt. Patches werden jetzt eingereicht, kommentiert, von anderen Personen als dem ursprünglichen Einreicher überarbeitet und hin und her geschickt, bis der Patch für das Einchecken als würdig erachtet wird. Fehler werden an einem zentralen Ort verfolgt und können einer bestimmten Person zur Behebung zugewiesen werden, und wir können die Anzahl der offenen Fehler zählen, um den Fortschritt zu messen. Dies geschah nicht ohne Kosten: Entwickler haben jetzt mehr E-Mails zu bearbeiten, mehr Mailinglisten zu verfolgen, und es mussten spezielle Tools für die neue Umgebung geschrieben werden. So sendet SourceForge beispielsweise standardmäßig völlig unbrauchbare Patch- und Fehlerbenachrichtigungs-E-Mails, sodass Ka-Ping Yee einen HTML-Screen-Scraper schrieb, der nützlichere Nachrichten sendet.

Die einfache Möglichkeit, Code hinzuzufügen, führte zu einigen anfänglichen Wachstumsschmerzen, wie z. B. dass Code eingecheckt wurde, bevor er fertig war oder ohne klare Zustimmung der Entwicklergruppe. Der sich herausgebildete Genehmigungsprozess ähnelt eher dem der Apache-Gruppe. Entwickler können mit +1, +0, -0 oder -1 für einen Patch abstimmen; +1 und -1 bezeichnen Akzeptanz oder Ablehnung, während +0 und -0 bedeuten, dass der Entwickler der Änderung weitgehend gleichgültig gegenübersteht, wenn auch mit einer leichten positiven oder negativen Tendenz. Die wichtigste Änderung gegenüber dem Apache-Modell ist, dass die Abstimmung im Wesentlichen beratend ist und Guido van Rossum, der den Status des Benevolent Dictator For Life innehat, wissen lässt, was die allgemeine Meinung ist. Er kann das Ergebnis einer Abstimmung immer noch ignorieren und eine Änderung genehmigen oder ablehnen, auch wenn die Community anderer Meinung ist.

Die Erstellung eines tatsächlichen Patches ist der letzte Schritt bei der Hinzufügung einer neuen Funktion und ist normalerweise einfach im Vergleich zur früheren Aufgabe, ein gutes Design zu entwickeln. Diskussionen über neue Funktionen können oft in langwierige E-Mail-Threads ausarten, was die Diskussion schwer nachvollziehbar macht, und niemand kann jeden Beitrag zu python-dev lesen. Daher wurde ein relativ formeller Prozess zur Erstellung von Python Enhancement Proposals (PEPs) eingerichtet, der dem Internet-RFC-Prozess nachempfunden ist. PEPs sind Entwurfsdokumente, die eine vorgeschlagene neue Funktion beschreiben und kontinuierlich überarbeitet werden, bis die Community einen Konsens erreicht, der den Vorschlag entweder akzeptiert oder ablehnt. Zitat aus der Einleitung zu PEP 1, "PEP Purpose and Guidelines"

PEP steht für Python Enhancement Proposal. Ein PEP ist ein Design-Dokument, das Informationen für die Python-Community liefert oder eine neue Funktion für Python beschreibt. Der PEP sollte eine prägnante technische Spezifikation der Funktion und eine Begründung für die Funktion liefern.

Wir beabsichtigen, PEPs als primäre Mechanismen für die Vorschlagung neuer Funktionen, das Sammeln von Community-Feedback zu einem Thema und die Dokumentation der Design-Entscheidungen zu verwenden, die in Python eingeflossen sind. Der PEP-Autor ist dafür verantwortlich, Konsens innerhalb der Community aufzubauen und abweichende Meinungen zu dokumentieren.

Lesen Sie den Rest von PEP 1 für die Details des PEP-Redaktionsprozesses, Stils und Formats. PEPs werden im Python CVS-Tree auf SourceForge aufbewahrt, obwohl sie nicht Teil der Python 2.0-Distribution sind, und sind auch in HTML-Form unter https://peps.pythonlang.de/ verfügbar. Im September 2000 gibt es 25 PEPs, von PEP 201, "Lockstep Iteration", bis PEP 225, "Elementwise/Objectwise Operators".

Unicode

Die größte neue Funktion in Python 2.0 ist ein neuer grundlegender Datentyp: Unicode-Strings. Unicode verwendet 16-Bit-Zahlen zur Darstellung von Zeichen anstelle der 8-Bit-Zahlen, die von ASCII verwendet werden, was bedeutet, dass 65.536 verschiedene Zeichen unterstützt werden können.

Die endgültige Schnittstelle für die Unicode-Unterstützung wurde durch unzählige oft stürmische Diskussionen auf der python-dev Mailingliste erreicht und hauptsächlich von Marc-André Lemburg implementiert, basierend auf einer Unicode-String-Typ-Implementierung von Fredrik Lundh. Eine detaillierte Erklärung der Schnittstelle wurde als PEP 100, "Python Unicode Integration", aufgeschrieben. Dieser Artikel behandelt einfach die wichtigsten Punkte über die Unicode-Schnittstellen.

Im Python-Quellcode werden Unicode-Strings als u"string" geschrieben. Beliebige Unicode-Zeichen können mit einer neuen Escape-Sequenz, \uHHHH, geschrieben werden, wobei HHHH eine 4-stellige Hexadezimalzahl von 0000 bis FFFF ist. Die vorhandene Escape-Sequenz \xHH kann ebenfalls verwendet werden, und Oktal-Escapes können für Zeichen bis U+01FF verwendet werden, die durch \777 dargestellt werden.

Unicode-Strings sind, genau wie normale Strings, ein unveränderlicher Sequenztyp. Sie können indiziert und gesliced werden, aber nicht vor Ort geändert werden. Unicode-Strings haben eine encode( [encoding] ) Methode, die einen 8-Bit-String in der gewünschten Kodierung zurückgibt. Kodierungen werden durch Strings benannt, wie 'ascii', 'utf-8', 'iso-8859-1' oder was auch immer. Eine Codec-API ist definiert für die Implementierung und Registrierung neuer Kodierungen, die dann in einem Python-Programm verfügbar sind. Wenn keine Kodierung angegeben ist, ist die Standardkodierung normalerweise 7-Bit-ASCII, obwohl sie für Ihre Python-Installation geändert werden kann, indem Sie die Funktion sys.setdefaultencoding(encoding) in einer angepassten Version von site.py aufrufen.

Das Kombinieren von 8-Bit- und Unicode-Strings wird immer zu Unicode erzwungen, wobei die Standard-ASCII-Kodierung verwendet wird; das Ergebnis von 'a' + u'bc' ist u'abc'.

Neue eingebaute Funktionen wurden hinzugefügt, und bestehende eingebaute Funktionen wurden modifiziert, um Unicode zu unterstützen

  • unichr(ch) gibt einen Unicode-String der Länge 1 zurück, der das Zeichen ch enthält.

  • ord(u), wobei u ein einzelner regulärer oder Unicode-String ist, gibt die Nummer des Zeichens als Ganzzahl zurück.

  • unicode(string [, encoding]  [, errors] ) erstellt einen Unicode-String aus einem 8-Bit-String. encoding ist ein String, der die zu verwendende Kodierung benennt. Der Parameter errors gibt die Behandlung von Zeichen an, die für die aktuelle Kodierung ungültig sind; die Übergabe von 'strict' als Wert bewirkt, dass bei jedem Kodierungsfehler eine Ausnahme ausgelöst wird, während 'ignore' bewirkt, dass Fehler stillschweigend ignoriert werden, und 'replace' U+FFFD, das offizielle Ersatzzeichen, bei Problemen verwendet.

  • Die exec-Anweisung und verschiedene eingebaute Funktionen wie eval(), getattr() und setattr() akzeptieren ebenfalls Unicode-Strings sowie reguläre Strings. (Es ist möglich, dass bei der Behebung dieses Problems einige eingebaute Funktionen übersehen wurden; wenn Sie eine eingebaute Funktion finden, die Strings akzeptiert, aber überhaupt keine Unicode-Strings akzeptiert, melden Sie dies bitte als Fehler.)

Ein neues Modul, unicodedata, bietet eine Schnittstelle zu Unicode-Zeicheneigenschaften. Zum Beispiel gibt unicodedata.category(u'A') den 2-stelligen String 'Lu' zurück, wobei 'L' anzeigt, dass es sich um einen Buchstaben handelt, und 'u' bedeutet, dass er großgeschrieben ist. unicodedata.bidirectional(u'\u0660') gibt 'AN' zurück, was bedeutet, dass U+0660 eine arabische Zahl ist.

Das Modul codecs enthält Funktionen zum Nachschlagen bestehender Kodierungen und Registrieren neuer. Sofern Sie keine neue Kodierung implementieren möchten, werden Sie am häufigsten die Funktion codecs.lookup(encoding) verwenden, die ein 4-elementiges Tupel zurückgibt: (encode_func, decode_func, stream_reader, stream_writer).

  • encode_func ist eine Funktion, die einen Unicode-String entgegennimmt und ein 2-Tupel (string, length) zurückgibt. string ist ein 8-Bit-String, der einen Teil (möglicherweise den gesamten) des Unicode-Strings enthält, der in die gegebene Kodierung konvertiert wurde, und length gibt an, wie viel vom Unicode-String konvertiert wurde.

  • decode_func ist das Gegenteil von encode_func, nimmt einen 8-Bit-String entgegen und gibt ein 2-Tupel (ustring, length) zurück, bestehend aus dem resultierenden Unicode-String ustring und der Ganzzahl length, die angibt, wie viel vom 8-Bit-String verbraucht wurde.

  • stream_reader ist eine Klasse, die das Dekodieren von Eingaben aus einem Stream unterstützt. stream_reader(file_obj) gibt ein Objekt zurück, das die Methoden read(), readline() und readlines() unterstützt. Diese Methoden übersetzen alle aus der gegebenen Kodierung und geben Unicode-Strings zurück.

  • stream_writer ist eine Klasse, die das Kodieren von Ausgaben in einen Stream unterstützt. stream_writer(file_obj) gibt ein Objekt zurück, das die Methoden write() und writelines() unterstützt. Diese Methoden erwarten Unicode-Strings und übersetzen sie bei der Ausgabe in die gegebene Kodierung.

Zum Beispiel schreibt der folgende Code einen Unicode-String in eine Datei, wobei er als UTF-8 kodiert wird

import codecs

unistr = u'\u0660\u2000ab ...'

(UTF8_encode, UTF8_decode,
 UTF8_streamreader, UTF8_streamwriter) = codecs.lookup('UTF-8')

output = UTF8_streamwriter( open( '/tmp/output', 'wb') )
output.write( unistr )
output.close()

Der folgende Code würde dann UTF-8-Eingaben aus der Datei lesen

input = UTF8_streamreader( open( '/tmp/output', 'rb') )
print repr(input.read())
input.close()

Unicode-fähige reguläre Ausdrücke sind über das Modul re verfügbar, das eine neue zugrunde liegende Implementierung namens SRE von Fredrik Lundh von Secret Labs AB hat.

Eine Befehlszeilenoption -U wurde hinzugefügt, die den Python-Compiler veranlasst, alle String-Literale als Unicode-String-Literale zu interpretieren. Dies ist für das Testen und zukunftssichere Gestalten Ihres Python-Codes gedacht, da eine zukünftige Version von Python die Unterstützung für 8-Bit-Strings einstellen und nur Unicode-Strings bereitstellen könnte.

Listen-Komprehensionen

Listen sind ein Arbeitspferd-Datentyp in Python, und viele Programme manipulieren irgendwann eine Liste. Zwei gängige Operationen mit Listen sind das Durchlaufen, um entweder die Elemente auszuwählen, die einem bestimmten Kriterium entsprechen, oder eine Funktion auf jedes Element anzuwenden. Gegeben sei beispielsweise eine Liste von Strings, und Sie möchten alle Strings herausfiltern, die einen bestimmten Teilstring enthalten, oder nachgestellte Leerzeichen von jeder Zeile entfernen.

Die vorhandenen Funktionen map() und filter() können für diesen Zweck verwendet werden, erfordern jedoch eine Funktion als eines ihrer Argumente. Das ist in Ordnung, wenn eine vorhandene eingebaute Funktion direkt übergeben werden kann, aber wenn nicht, müssen Sie eine kleine Funktion erstellen, um die erforderliche Arbeit zu erledigen, und Pythons Geltungsbereichsregeln machen das Ergebnis unschön, wenn die kleine Funktion zusätzliche Informationen benötigt. Nehmen Sie das erste Beispiel im vorherigen Absatz, das alle Strings in der Liste findet, die einen gegebenen Teilstring enthalten. Sie könnten Folgendes schreiben, um es zu tun

# Given the list L, make a list of all strings
# containing the substring S.
sublist = filter( lambda s, substring=S:
                     string.find(s, substring) != -1,
                  L)

Aufgrund von Pythons Geltungsbereichsregeln wird ein Standardargument verwendet, damit die anonyme Funktion, die durch den lambda-Ausdruck erstellt wird, weiß, nach welchem Teilstring gesucht wird. Listen-Komprehensionen machen dies übersichtlicher

sublist = [ s for s in L if string.find(s, S) != -1 ]

Listen-Komprehensionen haben die Form

[ expression for expr in sequence1
             for expr2 in sequence2 ...
             for exprN in sequenceN
             if condition ]

Die forin-Klauseln enthalten die zu durchlaufenden Sequenzen. Die Sequenzen müssen nicht die gleiche Länge haben, da sie *nicht* parallel durchlaufen werden, sondern von links nach rechts; dies wird in den folgenden Absätzen klarer erklärt. Die Elemente der generierten Liste sind die aufeinanderfolgenden Werte von expression. Die letzte if-Klausel ist optional; wenn sie vorhanden ist, wird expression nur ausgewertet und dem Ergebnis hinzugefügt, wenn condition wahr ist.

Um die Semantik sehr klar zu machen, ist eine Listen-Komprehension äquivalent zu folgendem Python-Code

for expr1 in sequence1:
    for expr2 in sequence2:
    ...
        for exprN in sequenceN:
             if (condition):
                  # Append the value of
                  # the expression to the
                  # resulting list.

Das bedeutet, dass bei mehreren forin-Klauseln die resultierende Liste gleich dem Produkt der Längen aller Sequenzen ist. Wenn Sie zwei Listen der Länge 3 haben, ist die Ausgabeliste 9 Elemente lang

seq1 = 'abc'
seq2 = (1,2,3)
>>> [ (x,y) for x in seq1 for y in seq2]
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1),
('c', 2), ('c', 3)]

Um eine Mehrdeutigkeit in Pythons Grammatik zu vermeiden, muss expression von Klammern umgeben sein, wenn ein Tupel erstellt wird. Die erste Listen-Komprehension unten ist ein Syntaxfehler, während die zweite korrekt ist

# Syntax error
[ x,y for x in seq1 for y in seq2]
# Correct
[ (x,y) for x in seq1 for y in seq2]

Die Idee der Listen-Komprehensionen stammt ursprünglich aus der funktionalen Programmiersprache Haskell (https://www.haskell.org). Greg Ewing argumentierte am überzeugendsten für deren Aufnahme in Python und schrieb den ursprünglichen Listen-Komprehension-Patch, der dann scheinbar endlos auf der python-dev Mailingliste diskutiert und von Skip Montanaro auf dem neuesten Stand gehalten wurde.

Erweiterte Zuweisung

Erweiterte Zuweisungsoperatoren, eine weitere lang geforderte Funktion, wurden zu Python 2.0 hinzugefügt. Erweiterte Zuweisungsoperatoren umfassen +=, -=, *= und so weiter. Beispielsweise inkrementiert die Anweisung a += 2 den Wert der Variablen a um 2, was dem etwas längeren a = a + 2 entspricht.

Die vollständige Liste der unterstützten Zuweisungsoperatoren ist +=, -=, *=, /=, %=, **=, &=, |=, ^=, >>= und <<=. Python-Klassen können die erweiterten Zuweisungsoperatoren überschreiben, indem sie Methoden namens __iadd__(), __isub__() usw. definieren. Zum Beispiel speichert die folgende Number-Klasse eine Zahl und unterstützt die Verwendung von += zum Erstellen einer neuen Instanz mit einem inkrementierten Wert.

class Number:
    def __init__(self, value):
        self.value = value
    def __iadd__(self, increment):
        return Number( self.value + increment)

n = Number(5)
n += 3
print n.value

Die spezielle Methode __iadd__() wird mit dem Wert der Inkrementierung aufgerufen und sollte eine neue Instanz mit einem entsprechend modifizierten Wert zurückgeben; dieser Rückgabewert wird als neuer Wert der Variablen auf der linken Seite gebunden.

Erweiterte Zuweisungsoperatoren wurden erstmals in der C-Programmiersprache eingeführt, und die meisten C-abgeleiteten Sprachen wie awk, C++, Java, Perl und PHP unterstützen sie ebenfalls. Der Patch für die erweiterte Zuweisung wurde von Thomas Wouters implementiert.

String-Methoden

Bis dahin befand sich die Funktionalität zur String-Manipulation im Modul string, das normalerweise ein Frontend für das in C geschriebene Modul strop war. Die Hinzufügung von Unicode stellte eine Schwierigkeit für das Modul strop dar, da alle Funktionen neu geschrieben werden müssten, um entweder 8-Bit- oder Unicode-Strings zu akzeptieren. Für Funktionen wie string.replace(), die 3 String-Argumente entgegennehmen, bedeutet dies acht mögliche Permutationen und entsprechend komplizierten Code.

Stattdessen verschiebt Python 2.0 das Problem auf den String-Typ und macht die Funktionalität zur String-Manipulation über Methoden auf sowohl 8-Bit-Strings als auch Unicode-Strings verfügbar.

>>> 'andrew'.capitalize()
'Andrew'
>>> 'hostname'.replace('os', 'linux')
'hlinuxtname'
>>> 'moshe'.find('sh')
2

Eines ist unverändert geblieben, ungeachtet eines bemerkenswerten Aprilscherzes: Python-Strings sind unveränderlich. Daher geben die String-Methoden neue Strings zurück und ändern nicht den String, auf dem sie operieren.

Das alte Modul string ist weiterhin für die Abwärtskompatibilität vorhanden, fungiert aber meist als Frontend für die neuen String-Methoden.

Zwei Methoden, die in Versionen vor 2.0 keine Parallele haben, obwohl sie in JPython schon lange existieren, sind startswith() und endswith(). s.startswith(t) ist äquivalent zu s[:len(t)] == t, während s.endswith(t) äquivalent zu s[-len(t):] == t ist.

Eine weitere Methode, die besondere Erwähnung verdient, ist join(). Die join()-Methode eines Strings nimmt einen Parameter entgegen, eine Sequenz von Strings, und ist äquivalent zur Funktion string.join() aus dem alten Modul string, mit vertauschten Argumenten. Mit anderen Worten, s.join(seq) ist äquivalent zum alten string.join(seq, s).

Garbage Collection von Zyklen

Die C-Implementierung von Python verwendet Referenzzählung zur Implementierung der Garbage Collection. Jedes Python-Objekt verwaltet eine Zählung der Anzahl der Referenzen, die auf sich selbst zeigen, und passt die Zählung an, wenn Referenzen erstellt oder zerstört werden. Sobald die Referenzanzahl Null erreicht, ist das Objekt nicht mehr zugänglich, da man eine Referenz auf ein Objekt haben muss, um es zu erreichen, und wenn die Zählung Null ist, existieren keine Referenzen mehr.

Referenzzählung hat einige angenehme Eigenschaften: Sie ist leicht zu verstehen und zu implementieren, und die resultierende Implementierung ist portabel, relativ schnell und reagiert gut mit anderen Bibliotheken, die ihre eigenen Speicherverwaltungsstrategien implementieren. Das Hauptproblem der Referenzzählung ist, dass sie manchmal nicht erkennt, dass Objekte nicht mehr zugänglich sind, was zu einem Speicherleck führt. Dies geschieht, wenn es Referenzzyklen gibt.

Betrachten Sie den einfachsten möglichen Zyklus, eine Instanz einer Klasse, die eine Referenz auf sich selbst hat

instance = SomeClass()
instance.myself = instance

Nachdem die obigen beiden Codezeilen ausgeführt wurden, ist die Referenzanzahl von instance 2; eine Referenz stammt von der Variable 'instance', und die andere stammt vom Attribut myself der Instanz.

Wenn die nächste Codezeile del instance lautet, was passiert dann? Die Referenzanzahl von instance wird um 1 reduziert, sodass sie eine Referenzanzahl von 1 hat; die Referenz im Attribut myself existiert noch. Doch die Instanz ist durch Python-Code nicht mehr zugänglich und könnte gelöscht werden. Mehrere Objekte können an einem Zyklus teilnehmen, wenn sie Referenzen aufeinander haben, was dazu führt, dass alle Objekte lecken.

Python 2.0 behebt dieses Problem, indem es periodisch einen Zykluserkennungsalgorithmus ausführt, der nach nicht zugänglichen Zyklen sucht und die beteiligten Objekte löscht. Ein neues Modul gc bietet Funktionen zur Durchführung einer Garbage Collection, zum Abrufen von Debugging-Statistiken und zur Feinabstimmung der Parameter des Collectors.

Das Ausführen des Zykluserkennungsalgorithmus dauert einige Zeit und führt daher zu zusätzlichem Overhead. Es ist zu hoffen, dass nach Erhalt von Erfahrungen mit der Zyklussammlung durch die Nutzung von 2.0 Python 2.1 in der Lage sein wird, den Overhead durch sorgfältige Abstimmung zu minimieren. Es ist noch nicht klar, wie viel Leistung verloren geht, da das Benchmarking schwierig ist und entscheidend davon abhängt, wie oft das Programm Objekte erstellt und zerstört. Die Erkennung von Zyklen kann deaktiviert werden, wenn Python kompiliert wird, wenn Sie sich keine winzige Geschwindigkeitsbuße leisten können oder vermuten, dass die Zyklussammlung fehlerhaft ist, indem Sie den Schalter --without-cycle-gc beim Ausführen des configure-Skripts angeben.

Mehrere Personen haben sich diesem Problem angenommen und zu einer Lösung beigetragen. Eine frühe Implementierung des Zykluserkennungsansatzes wurde von Toby Kelsey geschrieben. Der aktuelle Algorithmus wurde von Eric Tiedemann während eines Besuchs bei CNRI vorgeschlagen, und Guido van Rossum und Neil Schemenauer schrieben zwei verschiedene Implementierungen, die später von Neil integriert wurden. Viele andere Leute gaben Vorschläge ab; die Archive der python-dev Mailingliste vom März 2000 enthalten die meisten relevanten Diskussionen, insbesondere in den Threads mit dem Titel "Reference cycle collection for Python" und "Finalization again".

Andere Kernänderungen

Verschiedene kleinere Änderungen wurden an Pythons Syntax und eingebauten Funktionen vorgenommen. Keine der Änderungen ist sehr weitreichend, aber sie sind praktische Annehmlichkeiten.

Kleinere Sprachänderungen

Eine neue Syntax macht es bequemer, eine gegebene Funktion mit einem Tupel von Argumenten und/oder einem Dictionary von Schlüsselwortargumenten aufzurufen. In Python 1.5 und früheren Versionen würden Sie die eingebaute Funktion apply() verwenden: apply(f, args, kw) ruft die Funktion f() mit dem Argumenttupel args und den Schlüsselwortargumenten im Dictionary kw auf. apply() ist in 2.0 dasselbe, aber dank eines Patches von Greg Ewing ist f(*args, **kw) ein kürzerer und klarerer Weg, denselben Effekt zu erzielen. Diese Syntax ist symmetrisch zur Syntax für die Definition von Funktionen

def f(*args, **kw):
    # args is a tuple of positional args,
    # kw is a dictionary of keyword args
    ...

Die print-Anweisung kann nun ihre Ausgabe auf ein dateiähnliches Objekt umleiten, indem sie auf print >> file folgt, ähnlich dem Umleitungsoperator in Unix-Shells. Zuvor mussten Sie entweder die write()-Methode des dateiähnlichen Objekts verwenden, der es an Komfort und Einfachheit von print mangelt, oder Sie konnten sys.stdout einen neuen Wert zuweisen und dann den alten Wert wiederherstellen. Für die Ausgabe nach Standardfehler ist es viel einfacher, dies zu schreiben

print >> sys.stderr, "Warning: action field not supplied"

Module können nun beim Importieren umbenannt werden, indem die Syntax import module as name oder from module import name as othername verwendet wird. Der Patch wurde von Thomas Wouters eingereicht.

Ein neuer Formatstil ist bei der Verwendung des %-Operators verfügbar; '%r' fügt das repr() seines Arguments ein. Dies wurde auch aus Symmetriegründen hinzugefügt, diesmal zur Symmetrie mit dem vorhandenen '%s'-Formatstil, der das str() seines Arguments einfügt. Zum Beispiel gibt '%r %s' % ('abc', 'abc') einen String zurück, der 'abc' abc enthält.

Zuvor gab es keine Möglichkeit, eine Klasse zu implementieren, die den eingebauten in-Operator von Python überschrieb und eine benutzerdefinierte Version implementierte. obj in seq gibt wahr zurück, wenn obj in der Sequenz seq vorhanden ist; Python berechnet dies, indem es einfach jeden Index der Sequenz ausprobiert, bis entweder obj gefunden wird oder ein IndexError auftritt. Moshe Zadka trug einen Patch bei, der eine magische Methode __contains__() hinzufügt, um eine benutzerdefinierte Implementierung für in bereitzustellen. Zusätzlich können neue eingebaute Objekte, die in C geschrieben sind, über einen neuen Slot im Sequenzprotokoll definieren, was in für sie bedeutet.

Frühere Versionen von Python verwendeten einen rekursiven Algorithmus zum Löschen von Objekten. Tief verschachtelte Datenstrukturen konnten dazu führen, dass der Interpreter den C-Stack füllte und abstürzte; Christian Tismer schrieb die Löschlogik um, um dieses Problem zu beheben. In einem ähnlichen Zusammenhang führten Vergleiche rekursiver Objekte zu einer unendlichen Rekursion und zum Absturz; Jeremy Hylton schrieb den Code so um, dass er nicht mehr abstürzte, sondern ein nützliches Ergebnis lieferte. Zum Beispiel, nach diesem Code

a = []
b = []
a.append(a)
b.append(b)

Der Vergleich a==b gibt true zurück, da die beiden rekursiven Datenstrukturen isomorph sind. Siehe den Thread „trashcan and PR#7“ in den Archiven der python-dev Mailingliste vom April 2000 für die Diskussion, die zu dieser Implementierung führte, und einige nützliche relevante Links. Beachten Sie, dass Vergleiche jetzt auch Ausnahmen auslösen können. In früheren Versionen von Python lieferte eine Vergleichsoperation wie cmp(a,b) immer eine Antwort, auch wenn eine benutzerdefinierte __cmp__() Methode einen Fehler auslöste, da die resultierende Ausnahme einfach stillschweigend verschluckt würde.

Es wurden Arbeiten zur Portierung von Python auf 64-Bit-Windows auf dem Itanium-Prozessor geleistet, hauptsächlich von Trent Mick von ActiveState. (Verwirrenderweise ist sys.platform auf Win64 immer noch 'win32', da es scheint, dass MS Visual C++ aus Gründen der Portierungsfreundlichkeit Code auf Itanium als 32-Bit behandelt.) PythonWin unterstützt auch Windows CE; siehe die Python CE-Seite unter https://pythonce.sourceforge.net/ für weitere Informationen.

Eine weitere neue Plattform ist Darwin/MacOS X; die anfängliche Unterstützung dafür ist in Python 2.0 enthalten. Dynamisches Laden funktioniert, wenn Sie „configure –with-dyld –with-suffix=.x“ angeben. Konsultieren Sie das README in der Python-Quellcodeverteilung für weitere Anweisungen.

Es wurde versucht, eine der „Warzen“ von Python zu beseitigen, die oft verwirrende NameError Ausnahme, wenn der Code auf eine lokale Variable verweist, bevor die Variable einen Wert zugewiesen bekommen hat. Zum Beispiel löst der folgende Code im print Statement sowohl in 1.5.2 als auch in 2.0 eine Ausnahme aus; in 1.5.2 wird eine NameError Ausnahme ausgelöst, während 2.0 eine neue UnboundLocalError Ausnahme auslöst. UnboundLocalError ist eine Unterklasse von NameError, sodass bestehender Code, der erwartet, dass NameError ausgelöst wird, weiterhin funktionieren sollte.

def f():
    print "i=",i
    i = i + 1
f()

Zwei neue Ausnahmen, TabError und IndentationError, wurden eingeführt. Sie sind beide Unterklassen von SyntaxError und werden ausgelöst, wenn Python-Code falsch eingerückt ist.

Änderungen an eingebauten Funktionen

Eine neue eingebaute Funktion, zip(seq1, seq2, ...), wurde hinzugefügt. zip() gibt eine Liste von Tupeln zurück, wobei jedes Tupel das i-te Element aus jeder der Argumentsequenzen enthält. Der Unterschied zwischen zip() und map(None, seq1, seq2) besteht darin, dass map() die Sequenzen mit None auffüllt, wenn die Sequenzen nicht alle die gleiche Länge haben, während zip() die zurückgegebene Liste auf die Länge der kürzesten Argumentsequenz kürzt.

Die Funktionen int() und long() akzeptieren jetzt einen optionalen „base“-Parameter, wenn das erste Argument ein String ist. int('123', 10) gibt 123 zurück, während int('123', 16) 291 zurückgibt. int(123, 16) löst eine TypeError Ausnahme mit der Meldung „can’t convert non-string with explicit base“ aus.

Eine neue Variable, die detailliertere Versionsinformationen enthält, wurde dem sys-Modul hinzugefügt. sys.version_info ist ein Tupel (major, minor, micro, level, serial). Zum Beispiel wäre in einem hypothetischen 2.0.1beta1 sys.version_info (2, 0, 1, 'beta', 1). *level* ist ein String wie "alpha", "beta" oder "final" für eine finale Veröffentlichung.

Dictionaries haben eine neue, ungewöhnliche Methode, setdefault(key, default), die sich ähnlich wie die vorhandene get() Methode verhält. Wenn jedoch der Schlüssel fehlt, gibt setdefault() sowohl den Wert von *default* zurück, wie get() es tun würde, als auch fügt ihn als Wert für *key* in das Dictionary ein. Daher können die folgenden Codezeilen

if dict.has_key( key ): return dict[key]
else:
    dict[key] = []
    return dict[key]

auf eine einzige return dict.setdefault(key, []) Anweisung reduziert werden.

Der Interpreter legt eine maximale Rekursionstiefe fest, um außer Kontrolle geratene Rekursionen abzufangen, bevor der C-Stack gefüllt wird und ein Core-Dump oder GPF verursacht wird. Zuvor war diese Grenze beim Kompilieren von Python festgelegt, aber in 2.0 kann die maximale Rekursionstiefe mit sys.getrecursionlimit() und sys.setrecursionlimit() gelesen und geändert werden. Der Standardwert ist 1000, und ein grober Maximalwert für eine gegebene Plattform kann durch Ausführen eines neuen Skripts, Misc/find_recursionlimit.py, gefunden werden.

Portierung auf 2.0

Neue Python-Versionen bemühen sich stark, mit früheren Versionen kompatibel zu sein, und die Bilanz war ziemlich gut. Einige Änderungen werden jedoch als nützlich genug erachtet, meist weil sie anfängliche Designentscheidungen korrigieren, die sich als aktiv fehlerhaft erwiesen haben, dass eine Beeinträchtigung der Abwärtskompatibilität nicht immer vermieden werden kann. Dieser Abschnitt listet die Änderungen in Python 2.0 auf, die alten Python-Code brechen könnten.

Die wahrscheinlich den meisten Code brechende Änderung ist die Verschärfung der Argumente, die von einigen Methoden akzeptiert werden. Einige Methoden nahmen mehrere Argumente und behandelten sie als Tupel, insbesondere verschiedene Listenmethoden wie append() und insert(). In früheren Versionen von Python, wenn L eine Liste ist, fügt L.append( 1,2 ) das Tupel (1,2) zur Liste hinzu. In Python 2.0 löst dies eine TypeError Ausnahme mit der Meldung „append requires exactly 1 argument; 2 given“ aus. Die Korrektur besteht darin, einfach eine zusätzliche Klammer hinzuzufügen, um beide Werte als Tupel zu übergeben: L.append( (1,2) ).

Die früheren Versionen dieser Methoden waren nachsichtiger, da sie eine alte Funktion in Pythons C-Schnittstelle zur Analyse ihrer Argumente verwendeten; 2.0 modernisiert sie zur Verwendung von PyArg_ParseTuple(), der aktuellen Argumentanalysefunktion, die hilfreichere Fehlermeldungen liefert und Mehrfachargumentaufrufe als Fehler behandelt. Wenn Sie 2.0 unbedingt verwenden müssen, aber Ihren Code nicht reparieren können, können Sie Objects/listobject.c bearbeiten und das Präprozessorsymbol NO_STRICT_LIST_APPEND definieren, um das alte Verhalten beizubehalten; dies wird nicht empfohlen.

Einige Funktionen im socket-Modul sind in dieser Hinsicht immer noch nachsichtig. Zum Beispiel ist socket.connect( ('hostname', 25) ) die korrekte Form, die ein Tupel übergibt, das eine IP-Adresse darstellt, aber socket.connect('hostname', 25) funktioniert ebenfalls. socket.connect_ex und socket.bind sind ähnlich kulant. 2.0alpha1 verschärfte diese Funktionen, aber da die Dokumentation tatsächlich die fehlerhafte Mehrfachargumentform verwendete, schrieben viele Leute Code, der mit der strengeren Prüfung brechen würde. GvR zog die Änderungen angesichts der öffentlichen Reaktion zurück, sodass für das socket-Modul die Dokumentation korrigiert wurde und die Mehrfachargumentform einfach als veraltet markiert ist; sie wird in einer zukünftigen Python-Version *wieder* verschärft werden.

Der Escape-Sequenz \x in String-Literalen nimmt jetzt genau 2 Hex-Ziffern. Zuvor verbrauchte er alle Hex-Ziffern nach dem „x“ und nahm die niedrigsten 8 Bit des Ergebnisses, sodass \x123456 äquivalent zu \x56 war.

Die Ausnahmen AttributeError und NameError haben eine freundlichere Fehlermeldung, deren Text etwa 'Spam' instance has no attribute 'eggs' oder name 'eggs' is not defined lautet. Zuvor war die Fehlermeldung nur der fehlende Attributname eggs, und Code, der diese Tatsache ausnutzte, wird in 2.0 brechen.

Es wurde an der besseren Austauschbarkeit von Ganzzahlen und langen Ganzzahlen gearbeitet. In 1.5.2 wurde die Unterstützung für große Dateien für Solaris hinzugefügt, um das Lesen von Dateien größer als 2 GiB zu ermöglichen; dies führte dazu, dass die tell() Methode von Datei-Objekten eine lange Ganzzahl anstelle einer regulären Ganzzahl zurückgab. Einige Codes subtrahierten zwei Dateipositionen und versuchten, das Ergebnis zum Multiplizieren einer Sequenz oder zum Slicen eines Strings zu verwenden, aber dies löste eine TypeError Ausnahme aus. In 2.0 können lange Ganzzahlen zum Multiplizieren oder Slicen einer Sequenz verwendet werden, und sie verhalten sich intuitiv wie erwartet; 3L * 'abc' ergibt „abcabcabc“ und (0,1,2,3)[2L:4L] ergibt (2,3). Lange Ganzzahlen können auch in verschiedenen Kontexten verwendet werden, in denen zuvor nur Ganzzahlen akzeptiert wurden, wie z. B. in der seek() Methode von Datei-Objekten und in den vom % Operator unterstützten Formaten (%d, %i, %x usw.). Zum Beispiel ergibt "%d" % 2L**64 den String 18446744073709551616.

Die subtilste Änderung an langen Ganzzahlen von allen ist, dass die str() einer langen Ganzzahl kein nachgestelltes ‚L‘-Zeichen mehr hat, obwohl repr() es immer noch enthält. Das ‚L‘ ärgerte viele Leute, die lange Ganzzahlen drucken wollten, die wie reguläre Ganzzahlen aussahen, da sie sich bemühen mussten, das Zeichen abzuschneiden. Dies ist in 2.0 kein Problem mehr, aber Code, der str(longval)[:-1] verwendet und davon ausgeht, dass das ‚L‘ vorhanden ist, verliert jetzt die letzte Ziffer.

Das Nehmen des repr() eines Floats verwendet jetzt eine andere Formatpräzision als str(). repr() verwendet den Formatstring %.17g für sprintf() von C, während str() wie zuvor %.12g verwendet. Der Effekt ist, dass repr() bei bestimmten Zahlen gelegentlich mehr Dezimalstellen als str() anzeigen kann. Zum Beispiel kann die Zahl 8.1 nicht exakt in binärer Form dargestellt werden, sodass repr(8.1) '8.0999999999999996' ist, während str(8.1) '8.1' ist.

Die Befehlszeilenoption -X, die alle Standardausnahmen in Strings statt Klassen umwandelte, wurde entfernt; die Standardausnahmen sind nun immer Klassen. Das Modul exceptions, das die Standardausnahmen enthält, wurde von Python in ein integriertes C-Modul übersetzt, geschrieben von Barry Warsaw und Fredrik Lundh.

Änderungen an Erweiterung/Einbettung

Einige der Änderungen sind im Verborgenen und nur für Personen sichtbar, die C-Erweiterungsmodule schreiben oder einen Python-Interpreter in eine größere Anwendung einbetten. Wenn Sie sich nicht mit der C-API von Python beschäftigen, können Sie diesen Abschnitt sicher überspringen.

Die Versionsnummer der Python C-API wurde erhöht, sodass C-Erweiterungen, die für 1.5.2 kompiliert wurden, neu kompiliert werden müssen, um mit 2.0 zu funktionieren. Unter Windows ist es Python 2.0 nicht möglich, eine Drittanbietererweiterung zu importieren, die für Python 1.5.x erstellt wurde, aufgrund der Funktionsweise von Windows-DLLs, sodass Python eine Ausnahme auslöst und der Import fehlschlägt.

Benutzer von Jim Fultons ExtensionClass-Modul werden erfreut sein zu erfahren, dass Hooks hinzugefügt wurden, sodass ExtensionClasses nun von isinstance() und issubclass() unterstützt werden. Das bedeutet, dass Sie nicht mehr daran denken müssen, Code wie if type(obj) == myExtensionClass zu schreiben, sondern das natürlichere if isinstance(obj, myExtensionClass) verwenden können.

Die Datei Python/importdl.c, die eine Menge von #ifdefs zur Unterstützung des dynamischen Ladens auf vielen verschiedenen Plattformen enthielt, wurde von Greg Stein bereinigt und neu organisiert. importdl.c ist jetzt recht klein, und plattformspezifischer Code wurde in eine Reihe von Python/dynload_*.c Dateien verschoben. Eine weitere Bereinigung: Es gab auch eine Reihe von my*.h Dateien im Include/-Verzeichnis, die verschiedene Portierungs-Hacks enthielten; sie wurden in einer einzigen Datei zusammengefasst, Include/pyport.h.

Vladimir Marangozovs lang erwartete Malloc-Umstrukturierung wurde abgeschlossen, um es einfach zu machen, den Python-Interpreter einen benutzerdefinierten Allocator anstelle von C's Standard malloc() verwenden zu lassen. Zur Dokumentation lesen Sie die Kommentare in Include/pymem.h und Include/objimpl.h. Für die ausführlichen Diskussionen, während der die Schnittstelle ausgearbeitet wurde, siehe die Webarchive der Listen ‚patches‘ und ‚python-dev‘ unter python.org.

Neuere Versionen der GUSI-Entwicklungsumgebung für MacOS unterstützen POSIX-Threads. Daher funktioniert Pythons POSIX-Threading-Unterstützung jetzt auf dem Macintosh. Threading-Unterstützung mit der Benutzerraum-GNU pth Bibliothek wurde ebenfalls beigetragen.

Die Threading-Unterstützung unter Windows wurde ebenfalls verbessert. Windows unterstützt Thread-Locks, die Kernel-Objekte nur im Falle von Konflikten verwenden; im häufigen Fall, wenn es keine Konflikte gibt, verwenden sie einfachere Funktionen, die um eine Größenordnung schneller sind. Eine Thread-Version von Python 1.5.2 unter NT ist doppelt so langsam wie eine nicht-Thread-Version; mit den 2.0-Änderungen beträgt der Unterschied nur 10%. Diese Verbesserungen wurden von Yakov Markovitch beigesteuert.

Der Quellcode von Python 2.0 verwendet nun nur noch ANSI C-Prototypen, sodass das Kompilieren von Python nun einen ANSI C-Compiler erfordert und nicht mehr mit einem Compiler durchgeführt werden kann, der nur K&R C unterstützt.

Zuvor verwendete die Python-Virtuelle Maschine 16-Bit-Zahlen in ihrem Bytecode, was die Größe von Quellcodedateien begrenzte. Dies wirkte sich insbesondere auf die maximale Größe von Literallisten und Dictionaries in Python-Quellcode aus; gelegentlich stießen Personen, die Python-Code generierten, auf diese Grenze. Ein Patch von Charles G. Waldman erhöht die Grenze von 2**16 auf 2**32.

Drei neue Komfortfunktionen, die zur Laufzeit eines Moduls Konstanten zum Modul-Dictionary hinzufügen sollen, wurden hinzugefügt: PyModule_AddObject(), PyModule_AddIntConstant() und PyModule_AddStringConstant(). Jede dieser Funktionen nimmt ein Modulobjekt, einen null-terminierten C-String mit dem Namen, der hinzugefügt werden soll, und ein drittes Argument für den Wert, der dem Namen zugewiesen werden soll. Dieses dritte Argument ist jeweils ein Python-Objekt, ein C-long oder ein C-String.

Eine Wrapper-API für Unix-ähnliche Signalhandler wurde hinzugefügt. PyOS_getsig() holt einen Signalhandler und PyOS_setsig() setzt einen neuen Handler.

Distutils: Module einfach zu installieren machen

Vor Python 2.0 war die Installation von Modulen eine mühsame Angelegenheit – es gab keine Möglichkeit, automatisch zu ermitteln, wo Python installiert ist, oder welche Compiler-Optionen für Erweiterungsmodule verwendet werden sollten. Softwareautoren mussten ein mühsames Ritual des Bearbeitens von Makefiles und Konfigurationsdateien durchlaufen, das nur unter Unix wirklich funktionierte und Windows und MacOS ununterstützt ließ. Python-Benutzer sahen sich mit stark unterschiedlichen Installationsanweisungen konfrontiert, die sich zwischen verschiedenen Erweiterungspaketen unterschieden, was die Verwaltung einer Python-Installation zu einer ziemlichen Bürde machte.

Die SIG für Distributionswerkzeuge, betreut von Greg Ward, hat die Distutils geschaffen, ein System zur Vereinfachung der Paketinstallation. Sie bilden das distutils-Paket, ein neuer Teil der Python-Standardbibliothek. Im besten Fall erfordert die Installation eines Python-Moduls aus dem Quellcode die gleichen Schritte: Zuerst entpacken Sie einfach das Tarball- oder Zip-Archiv und führen dann „python setup.py install“ aus. Die Plattform wird automatisch erkannt, der Compiler erkannt, C-Erweiterungsmodule werden kompiliert und die Distribution wird in das richtige Verzeichnis installiert. Optionale Befehlszeilenargumente bieten mehr Kontrolle über den Installationsprozess, das distutils-Paket bietet viele Stellen zur Überschreibung von Standardeinstellungen – Trennung von Build und Install, Build oder Install in Nicht-Standard-Verzeichnissen und mehr.

Um Distutils zu verwenden, müssen Sie ein setup.py-Skript schreiben. Für den einfachen Fall, wenn die Software nur .py-Dateien enthält, kann ein minimales setup.py nur wenige Zeilen lang sein

from distutils.core import setup
setup (name = "foo", version = "1.0",
       py_modules = ["module1", "module2"])

Die Datei setup.py ist nicht viel komplizierter, wenn die Software aus mehreren Paketen besteht

from distutils.core import setup
setup (name = "foo", version = "1.0",
       packages = ["package", "package.subpackage"])

Eine C-Erweiterung kann der komplizierteste Fall sein; hier ist ein Beispiel aus dem PyXML-Paket

from distutils.core import setup, Extension

expat_extension = Extension('xml.parsers.pyexpat',
     define_macros = [('XML_NS', None)],
     include_dirs = [ 'extensions/expat/xmltok',
                      'extensions/expat/xmlparse' ],
     sources = [ 'extensions/pyexpat.c',
                 'extensions/expat/xmltok/xmltok.c',
                 'extensions/expat/xmltok/xmlrole.c', ]
       )
setup (name = "PyXML", version = "0.5.4",
       ext_modules =[ expat_extension ] )

Die Distutils können auch die Erstellung von Quell- und Binärdistributionen übernehmen. Der Befehl „sdist“, ausgeführt mit „python setup.py sdist“, erstellt eine Quellcode-Distribution wie foo-1.0.tar.gz. Das Hinzufügen neuer Befehle ist nicht schwierig, „bdist_rpm“ und „bdist_wininst“-Befehle wurden bereits beigesteuert, um eine RPM-Distribution und einen Windows-Installer für die Software zu erstellen. Befehle zur Erstellung anderer Distributionsformate wie Debian-Pakete und Solaris .pkg-Dateien befinden sich in verschiedenen Entwicklungsstadien.

All dies ist in einem neuen Handbuch, Distributing Python Modules, dokumentiert, das sich in den grundlegenden Python-Dokumentationssatz einfügt.

XML-Module

Python 1.5.2 enthielt einen einfachen XML-Parser in Form des xmllib-Moduls, beigesteuert von Sjoerd Mullender. Seit der Veröffentlichung von 1.5.2 sind zwei verschiedene Schnittstellen zur Verarbeitung von XML üblich geworden: SAX2 (Version 2 der Simple API for XML) bietet eine ereignisgesteuerte Schnittstelle mit einigen Ähnlichkeiten zu xmllib, und das DOM (Document Object Model) bietet eine baumbasierte Schnittstelle, die ein XML-Dokument in einen Baum von Knoten umwandelt, der durchlaufen und modifiziert werden kann. Python 2.0 enthält eine SAX2-Schnittstelle und eine abgespeckte DOM-Schnittstelle als Teil des xml-Pakets. Hier geben wir einen kurzen Überblick über diese neuen Schnittstellen; die vollständigen Details entnehmen Sie der Python-Dokumentation oder dem Quellcode. Die Python XML SIG arbeitet auch an einer verbesserten Dokumentation.

SAX2-Unterstützung

SAX definiert eine ereignisgesteuerte Schnittstelle zum Parsen von XML. Um SAX zu verwenden, müssen Sie eine SAX-Handler-Klasse schreiben. Handler-Klassen erben von verschiedenen von SAX bereitgestellten Klassen und überschreiben verschiedene Methoden, die dann vom XML-Parser aufgerufen werden. Zum Beispiel werden die Methoden startElement() und endElement() für jeden von dem Parser gefundenen Start- und End-Tag aufgerufen, die Methode characters() wird für jeden Block von Zeichendaten aufgerufen, und so weiter.

Der Vorteil des ereignisgesteuerten Ansatzes ist, dass das gesamte Dokument nicht jederzeit im Speicher vorhanden sein muss, was wichtig ist, wenn Sie sehr große Dokumente verarbeiten. Das Schreiben der SAX-Handler-Klasse kann jedoch sehr kompliziert werden, wenn Sie versuchen, die Dokumentenstruktur auf eine ausgeklügelte Weise zu ändern.

Zum Beispiel definiert dieses kleine Beispielprogramm einen Handler, der eine Nachricht für jedes Start- und End-Tag ausgibt, und parst dann die Datei hamlet.xml damit

from xml import sax

class SimpleHandler(sax.ContentHandler):
    def startElement(self, name, attrs):
        print 'Start of element:', name, attrs.keys()

    def endElement(self, name):
        print 'End of element:', name

# Create a parser object
parser = sax.make_parser()

# Tell it what handler to use
handler = SimpleHandler()
parser.setContentHandler( handler )

# Parse a file!
parser.parse( 'hamlet.xml' )

Weitere Informationen finden Sie in der Python-Dokumentation oder im XML HOWTO unter https://pyxml.sourceforge.net/topics/howto/xml-howto.html.

DOM-Unterstützung

Das Document Object Model ist eine baumbasierte Darstellung eines XML-Dokuments. Eine Top-Level Document Instanz ist die Wurzel des Baums und hat ein einziges Kind, die Top-Level Element Instanz. Dieses Element hat Kindknoten, die Zeichendaten und alle Unterelemente darstellen, die ihrerseits weitere Kinder haben können und so weiter. Mit dem DOM können Sie den resultierenden Baum beliebig durchlaufen, Element- und Attributwerte abrufen, Knoten einfügen und löschen und den Baum zurück in XML konvertieren.

Das DOM ist nützlich für die Modifikation von XML-Dokumenten, da Sie einen DOM-Baum erstellen, ihn durch Hinzufügen neuer Knoten oder Umordnen von Teilbäumen modifizieren und dann ein neues XML-Dokument als Ausgabe erzeugen können. Sie können auch manuell einen DOM-Baum erstellen und ihn in XML konvertieren, was eine flexiblere Methode zur Erzeugung von XML-Ausgaben sein kann, als einfach nur <tag1></tag1> in eine Datei zu schreiben.

Die in Python enthaltene DOM-Implementierung befindet sich im Modul xml.dom.minidom. Es handelt sich um eine leichtgewichtige Implementierung des Level 1 DOM mit Unterstützung für XML-Namensräume. Die Komfortfunktionen parse() und parseString() werden zur Erzeugung eines DOM-Baums bereitgestellt

from xml.dom import minidom
doc = minidom.parse('hamlet.xml')

doc ist eine Document-Instanz. Document ist, wie alle anderen DOM-Klassen wie Element und Text, eine Unterklasse der Basisklasse Node. Alle Knoten in einem DOM-Baum unterstützen daher bestimmte gemeinsame Methoden, wie z. B. toxml(), die einen String mit der XML-Darstellung des Knotens und seiner Kinder zurückgibt. Jede Klasse hat auch ihre eigenen speziellen Methoden; zum Beispiel haben Element- und Document-Instanzen eine Methode, um alle Kindelemente mit einem bestimmten Tag-Namen zu finden. Fortsetzung des vorherigen 2-Zeilen-Beispiels

perslist = doc.getElementsByTagName( 'PERSONA' )
print perslist[0].toxml()
print perslist[1].toxml()

Für die XML-Datei *Hamlet* geben die obigen Zeilen Folgendes aus

<PERSONA>CLAUDIUS, king of Denmark. </PERSONA>
<PERSONA>HAMLET, son to the late, and nephew to the present king.</PERSONA>

Das Wurzelelement des Dokuments ist über doc.documentElement verfügbar, und seine Kinder können leicht geändert werden, indem Knoten gelöscht, hinzugefügt oder entfernt werden

root = doc.documentElement

# Remove the first child
root.removeChild( root.childNodes[0] )

# Move the new first child to the end
root.appendChild( root.childNodes[0] )

# Insert the new first child (originally,
# the third child) before the 20th child.
root.insertBefore( root.childNodes[0], root.childNodes[20] )

Auch hier verweise ich Sie auf die Python-Dokumentation für eine vollständige Auflistung der verschiedenen Node-Klassen und ihrer verschiedenen Methoden.

Beziehung zu PyXML

Die XML Special Interest Group arbeitet seit einiger Zeit an XML-bezogenem Python-Code. Ihre Code-Distribution namens PyXML ist auf den Webseiten der SIG unter https://pythonlang.de/community/sigs/current/xml-sig verfügbar. Die PyXML-Distribution verwendete auch den Paketnamen xml. Wenn Sie Programme geschrieben haben, die PyXML verwendet haben, fragen Sie sich wahrscheinlich nach seiner Kompatibilität mit dem xml-Paket von 2.0.

Die Antwort ist, dass das xml-Paket von Python 2.0 nicht mit PyXML kompatibel ist, aber durch die Installation einer aktuellen Version von PyXML kompatibel gemacht werden kann. Viele Anwendungen können mit der in Python 2.0 enthaltenen XML-Unterstützung auskommen, aber kompliziertere Anwendungen erfordern, dass das vollständige PyXML-Paket installiert ist. Wenn installiert, ersetzen PyXML-Versionen 0.6.0 oder höher das mit Python ausgelieferte xml-Paket und sind eine strikte Obermenge des Standardpakets und fügen eine Reihe zusätzlicher Funktionen hinzu. Einige der zusätzlichen Funktionen in PyXML sind

  • 4DOM, eine vollständige DOM-Implementierung von FourThought, Inc.

  • Der validierende Parser xmlproc, geschrieben von Lars Marius Garshol.

  • Das Parser-Beschleunigungsmodul sgmlop, geschrieben von Fredrik Lundh.

Moduländerungen

Viele Verbesserungen und Fehlerbehebungen wurden an der umfangreichen Standardbibliothek von Python vorgenommen; einige der betroffenen Module sind readline, ConfigParser, cgi, calendar, posix, readline, xmllib, aifc, chunk, wave, random, shelve und nntplib. Konsultieren Sie die CVS-Logs für die genauen Details von Patch zu Patch.

Brian Gallew trug die OpenSSL-Unterstützung für das Modul socket bei. OpenSSL ist eine Implementierung des Secure Socket Layer, der die über einen Socket gesendeten Daten verschlüsselt. Beim Kompilieren von Python können Sie Modules/Setup bearbeiten, um SSL-Unterstützung einzuschließen, was eine zusätzliche Funktion zum Modul socket hinzufügt: socket.ssl(socket, keyfile, certfile), die ein Socket-Objekt entgegennimmt und einen SSL-Socket zurückgibt. Die Module httplib und urllib wurden ebenfalls geändert, um https://-URLs zu unterstützen, obwohl noch niemand FTP oder SMTP über SSL implementiert hat.

Das Modul httplib wurde von Greg Stein neu geschrieben, um HTTP/1.1 zu unterstützen.

Die Abwärtskompatibilität mit der Version 1.5 von httplib ist gegeben, obwohl die Verwendung von HTTP/1.1-Funktionen wie Pipelining eine Umschreibung des Codes erfordert, um eine andere Schnittstellengruppe zu verwenden.

Das Modul Tkinter unterstützt jetzt Tcl/Tk Version 8.1, 8.2 oder 8.3, und die Unterstützung für ältere 7.x-Versionen wurde eingestellt. Das Tkinter-Modul unterstützt jetzt die Anzeige von Unicode-Strings in Tk-Widgets. Außerdem trug Fredrik Lundh eine Optimierung bei, die Operationen wie create_line und create_polygon viel schneller macht, insbesondere bei vielen Koordinaten.

Das Modul curses wurde stark erweitert, beginnend mit Oliver Andrichs verbesserter Version, um viele zusätzliche Funktionen von ncurses und SYSV curses bereitzustellen, wie z. B. Farbe, Unterstützung für alternative Zeichensätze, Pads und Mausunterstützung. Das bedeutet, dass das Modul nicht mehr mit Betriebssystemen kompatibel ist, die nur BSD curses haben, aber es scheinen derzeit keine aktiv gepflegten Betriebssysteme zu geben, die in diese Kategorie fallen.

Wie in der früheren Diskussion der Unicode-Unterstützung von 2.0 erwähnt, wurde die zugrunde liegende Implementierung der regulären Ausdrücke, die vom Modul re bereitgestellt werden, geändert. SRE, eine neue Engine für reguläre Ausdrücke, die von Fredrik Lundh geschrieben und teilweise von Hewlett Packard finanziert wurde, unterstützt Abgleiche mit 8-Bit-Strings und Unicode-Strings.

Neue Module

Eine Reihe neuer Module wurden hinzugefügt. Wir listen sie einfach mit kurzen Beschreibungen auf; konsultieren Sie die Dokumentation von 2.0 für Details zu einem bestimmten Modul.

  • atexit: Zum Registrieren von Funktionen, die vor dem Beenden des Python-Interpreters aufgerufen werden sollen. Code, der derzeit sys.exitfunc direkt setzt, sollte geändert werden, um stattdessen das Modul atexit zu verwenden, atexit zu importieren und atexit.register() mit der aufzurufenden Funktion aufzurufen. (Beigetragen von Skip Montanaro.)

  • codecs, encodings, unicodedata: Hinzugefügt als Teil der neuen Unicode-Unterstützung.

  • filecmp: Ersetzt die alten Module cmp, cmpcache und dircmp, die nun als veraltet gelten. (Beigetragen von Gordon MacMillan und Moshe Zadka.)

  • gettext: Dieses Modul bietet Internationalisierungs- (I18N) und Lokalisierungs- (L10N) Unterstützung für Python-Programme, indem es eine Schnittstelle zur GNU gettext-Nachrichtenkatalogbibliothek bereitstellt. (Integriert von Barry Warsaw, aus separaten Beiträgen von Martin von Löwis, Peter Funk und James Henstridge.)

  • linuxaudiodev: Unterstützung für das Gerät /dev/audio unter Linux, ein Gegenstück zum vorhandenen Modul sunaudiodev. (Beigetragen von Peter Bosch, mit Korrekturen von Jeremy Hylton.)

  • mmap: Eine Schnittstelle zu speicherabbildeten Dateien unter Windows und Unix. Der Inhalt einer Datei kann direkt in den Speicher abgebildet werden, wo er sich wie ein veränderbarer String verhält, sodass sein Inhalt gelesen und geändert werden kann. Er kann sogar an Funktionen übergeben werden, die normale Strings erwarten, wie z. B. das Modul re. (Beigetragen von Sam Rushing, mit einigen Erweiterungen von A.M. Kuchling.)

  • pyexpat: Eine Schnittstelle zum Expat XML-Parser. (Beigetragen von Paul Prescod.)

  • robotparser: Parst eine robots.txt-Datei, die zum Schreiben von Web-Crawlern verwendet wird, die höflich bestimmte Bereiche einer Website meiden. Der Parser akzeptiert den Inhalt einer robots.txt-Datei, erstellt daraus eine Reihe von Regeln und kann dann Fragen zur Abrufbarkeit einer gegebenen URL beantworten. (Beigetragen von Skip Montanaro.)

  • tabnanny: Ein Modul/Skript zur Überprüfung von Python-Quellcode auf mehrdeutige Einrückungen. (Beigetragen von Tim Peters.)

  • UserString: Eine Basisklasse, die nützlich zum Ableiten von Objekten ist, die sich wie Strings verhalten.

  • webbrowser: Ein Modul, das eine plattformunabhängige Möglichkeit bietet, einen Webbrowser für eine bestimmte URL zu starten. Für jede Plattform werden verschiedene Browser in einer bestimmten Reihenfolge versucht. Der Benutzer kann ändern, welcher Browser gestartet wird, indem er die Umgebungsvariable BROWSER setzt. (Ursprünglich inspiriert von Eric S. Raymonds Patch zu urllib, der ähnliche Funktionalität hinzufügte, aber das endgültige Modul stammt aus Code, der ursprünglich von Fred Drake als Tools/idle/BrowserControl.py implementiert wurde, und für die Standardbibliothek von Fred angepasst wurde.)

  • _winreg: Eine Schnittstelle zur Windows-Registrierung. _winreg ist eine Anpassung von Funktionen, die seit 1995 Teil von PythonWin sind, aber nun in die Kernverteilung aufgenommen und zur Unterstützung von Unicode erweitert wurden. _winreg wurde von Bill Tutt und Mark Hammond geschrieben.

  • zipfile: Ein Modul zum Lesen und Schreiben von ZIP-Archivdateien. Dies sind Archive, die von PKZIP unter DOS/Windows oder zip unter Unix erstellt wurden, nicht zu verwechseln mit gzip-Format-Dateien (die vom Modul gzip unterstützt werden) (Beigetragen von James C. Ahlstrom.)

  • imputil: Ein Modul, das eine einfachere Möglichkeit bietet, benutzerdefinierte Import-Hooks zu schreiben, im Vergleich zum vorhandenen Modul ihooks. (Implementiert von Greg Stein, mit vielen Diskussionen auf python-dev zwischendurch.)

IDLE-Verbesserungen

IDLE ist die offizielle plattformübergreifende Python-IDE, die mit Tkinter erstellt wurde. Python 2.0 enthält IDLE 0.6, das eine Reihe neuer Funktionen und Verbesserungen hinzufügt. Eine Teilliste

  • UI-Verbesserungen und Optimierungen, insbesondere im Bereich Syntaxhervorhebung und automatische Einrückung.

  • Der Klassenbrowser zeigt nun mehr Informationen an, z. B. die Funktionen auf oberster Ebene in einem Modul.

  • Die Tabulatorbreite ist jetzt eine benutzerdefinierbare Option. Beim Öffnen einer vorhandenen Python-Datei erkennt IDLE automatisch die Einrückungskonventionen und passt sich an.

  • Es gibt nun Unterstützung für das Aufrufen von Browsern auf verschiedenen Plattformen, die zum Öffnen der Python-Dokumentation in einem Browser verwendet wird.

  • IDLE verfügt nun über eine Befehlszeile, die weitgehend dem normalen Python-Interpreter ähnelt.

  • Aufruf-Tipps wurden an vielen Stellen hinzugefügt.

  • IDLE kann nun als Paket installiert werden.

  • Im Editorfenster gibt es nun eine Zeilen-/Spaltenleiste am unteren Rand.

  • Drei neue Tastaturbefehle: Modul prüfen (Alt-F5), Modul importieren (F5) und Skript ausführen (Strg-F5).

Gelöschte und veraltete Module

Einige Module wurden gestrichen, weil sie veraltet sind oder weil es nun bessere Möglichkeiten gibt, dasselbe zu tun. Das Modul stdwin ist weg; es diente einem plattformunabhängigen GUI-Toolkit, das nicht mehr entwickelt wird.

Eine Reihe von Modulen wurde in das Unterverzeichnis lib-old verschoben: cmp, cmpcache, dircmp, dump, find, grep, packmail, poly, util, whatsound, zmod. Wenn Sie Code haben, der von einem Modul abhängt, das nach lib-old verschoben wurde, können Sie dieses Verzeichnis einfach zu sys.path hinzufügen, um sie zurückzubekommen, aber Sie werden ermutigt, jeden Code zu aktualisieren, der diese Module verwendet.

Danksagungen

Die Autoren möchten den folgenden Personen für ihre Vorschläge zu verschiedenen Entwürfen dieses Artikels danken: David Bolen, Mark Hammond, Gregg Hauser, Jeremy Hylton, Fredrik Lundh, Detlef Lannert, Aahz Maruch, Skip Montanaro, Vladimir Marangozov, Tobias Polzin, Guido van Rossum, Neil Schemenauer und Russ Schmidt.