Was ist neu in Python 2.2¶
- Autor:
A.M. Kuchling
Einleitung¶
Dieser Artikel erklärt die neuen Funktionen in Python 2.2.2, veröffentlicht am 14. Oktober 2002. Python 2.2.2 ist eine Fehlerbehebungs-Version von Python 2.2, ursprünglich veröffentlicht am 21. Dezember 2001.
Python 2.2 kann als die „Bereinigungsversion“ betrachtet werden. Es gibt einige Funktionen wie Generatoren und Iteratoren, die völlig neu sind, aber die meisten Änderungen, obwohl sie bedeutsam und weitreichend sein mögen, zielen darauf ab, Unregelmäßigkeiten und dunkle Ecken des Sprachdesigns zu bereinigen.
Dieser Artikel versucht nicht, eine vollständige Spezifikation der neuen Funktionen zu bieten, sondern stattdessen einen praktischen Überblick. Für vollständige Details sollten Sie die Dokumentation für Python 2.2 konsultieren, wie z. B. die Python Library Reference und das Python Reference Manual. Wenn Sie die vollständige Implementierung und die Designbegründung für eine Änderung verstehen möchten, konsultieren Sie das PEP für eine bestimmte neue Funktion.
PEPs 252 und 253: Typ- und Klassenänderungen¶
Die größten und weitreichendsten Änderungen in Python 2.2 betreffen Pythons Modell von Objekten und Klassen. Die Änderungen sollten abwärtskompatibel sein, so dass Ihr Code wahrscheinlich unverändert weiterläuft, aber die Änderungen bieten einige erstaunliche neue Möglichkeiten. Bevor ich mit diesem, dem längsten und kompliziertesten Abschnitt dieses Artikels, beginne, gebe ich einen Überblick über die Änderungen und einige Kommentare.
Vor langer Zeit habe ich eine Webseite mit Fehlern im Python-Design geschrieben. Einer der bedeutendsten Fehler war, dass es unmöglich ist, in C implementierte Python-Typen zu unterklassen. Insbesondere ist es nicht möglich, eingebaute Typen zu unterklassen, sodass Sie nicht einfach Listen unterklassen können, um ihnen eine einzige nützliche Methode hinzuzufügen. Das UserList-Modul bietet eine Klasse, die alle Methoden von Listen unterstützt und weiter unterklasset werden kann, aber es gibt viel C-Code, der eine normale Python-Liste erwartet und keine UserList-Instanz akzeptiert.
Python 2.2 behebt dies und fügt dabei einige aufregende neue Funktionen hinzu. Eine kurze Zusammenfassung
Sie können eingebaute Typen wie Listen und sogar ganze Zahlen unterklassen, und Ihre Unterklassen sollten an jeder Stelle funktionieren, an der der ursprüngliche Typ erforderlich ist.
Es ist nun möglich, statische und Klassenmethoden zu definieren, zusätzlich zu den Instanzmethoden, die in früheren Python-Versionen verfügbar waren.
Es ist auch möglich, Methoden beim Zugriff auf oder beim Setzen eines Instanzattributs automatisch aufzurufen, indem ein neuer Mechanismus namens Properties verwendet wird. Viele Verwendungen von
__getattr__()können durch die Verwendung von Properties ersetzt werden, wodurch der resultierende Code einfacher und schneller wird. Als kleiner Nebeneffekt können Attribute nun auch Docstrings haben.Die Liste der legalen Attribute für eine Instanz kann mit Slots auf eine bestimmte Menge beschränkt werden, wodurch Tippfehler vermieden und möglicherweise weitere Optimierungen in zukünftigen Python-Versionen ermöglicht werden.
Einige Benutzer haben Bedenken hinsichtlich all dieser Änderungen geäußert. Sicher, sagen sie, die neuen Funktionen sind nett und eignen sich für alle möglichen Tricks, die in früheren Python-Versionen nicht möglich waren, aber sie machen die Sprache auch komplizierter. Einige Leute haben gesagt, dass sie Python schon immer wegen seiner Einfachheit empfohlen haben und das Gefühl haben, dass seine Einfachheit verloren geht.
Persönlich denke ich, es gibt keinen Grund zur Sorge. Viele der neuen Funktionen sind ziemlich esoterisch, und Sie können viel Python-Code schreiben, ohne sich jemals dessen bewusst sein zu müssen. Das Schreiben einer einfachen Klasse ist nicht schwieriger als zuvor, sodass Sie sie nicht lernen oder lehren müssen, es sei denn, sie werden tatsächlich benötigt. Einige sehr komplizierte Aufgaben, die früher nur aus C möglich waren, werden nun in reinem Python möglich sein, und meiner Meinung nach ist das alles zum Besseren.
Ich werde nicht versuchen, jeden einzelnen Eckfall und jede kleine Änderung abzudecken, die zur Funktionsfähigkeit der neuen Features erforderlich waren. Stattdessen wird dieser Abschnitt nur die groben Züge malen. Siehe Abschnitt Verwandte Links, „Verwandte Links“, für weitere Informationsquellen über das neue Objektmodell von Python 2.2.
Alte und neue Klassen¶
Zunächst sollten Sie wissen, dass Python 2.2 eigentlich zwei Arten von Klassen hat: klassische oder altmodische Klassen und neuartige Klassen. Das altmodische Klassenmodell ist genau dasselbe wie das Klassenmodell in früheren Versionen von Python. Alle in diesem Abschnitt beschriebenen neuen Funktionen gelten nur für neuartige Klassen. Diese Divergenz soll nicht ewig dauern; irgendwann werden altmodische Klassen abgeschafft, möglicherweise in Python 3.0.
Wie definiert man also eine neuartige Klasse? Sie tun dies, indem Sie eine vorhandene neuartige Klasse unterklassen. Die meisten integrierten Typen von Python, wie z. B. Ganzzahlen, Listen, Wörterbücher und sogar Dateien, sind jetzt neuartige Klassen. Eine neuartige Klasse namens object, die Basisklasse für alle integrierten Typen, wurde ebenfalls hinzugefügt. Wenn also kein integrierter Typ geeignet ist, können Sie einfach object unterklassen.
class C(object):
def __init__ (self):
...
...
Das bedeutet, dass class-Anweisungen ohne Basisklassen in Python 2.2 immer klassische Klassen sind. (Tatsächlich können Sie dies auch ändern, indem Sie eine Modulvariable namens __metaclass__ festlegen – siehe PEP 253 für die Details – aber es ist einfacher, einfach object zu unterklassen.)
Die Typobjekte für die integrierten Typen sind als integrierte Objekte verfügbar, die mit einem cleveren Trick benannt werden. Python hatte schon immer integrierte Funktionen wie int(), float() und str(). In 2.2 sind dies keine Funktionen mehr, sondern Typobjekte, die sich beim Aufruf wie Fabriken verhalten.
>>> int
<type 'int'>
>>> int('123')
123
Um die Menge der Typen zu vervollständigen, wurden neue Typobjekte wie dict() und file() hinzugefügt. Hier ist ein interessanteres Beispiel: Hinzufügen einer lock()-Methode zu Datei-Objekten
class LockableFile(file):
def lock (self, operation, length=0, start=0, whence=0):
import fcntl
return fcntl.lockf(self.fileno(), operation,
length, start, whence)
Das nun veraltete Modul posixfile enthielt eine Klasse, die alle Methoden eines Datei-Objekts emulierte und auch eine lock()-Methode hinzufügte, aber diese Klasse konnte nicht an interne Funktionen übergeben werden, die eine integrierte Datei erwarteten, was mit unserer neuen LockableFile möglich ist.
Deskriptoren¶
In früheren Versionen von Python gab es keine konsistente Möglichkeit, zu erkennen, welche Attribute und Methoden ein Objekt unterstützte. Es gab einige informelle Konventionen, wie z. B. die Definition von __members__- und __methods__-Attributen, die Listen von Namen waren, aber oft nahm sich der Autor eines Erweiterungstyps oder einer Klasse keine Mühe, sie zu definieren. Sie konnten auf die Inspektion des __dict__ eines Objekts zurückgreifen, aber wenn Klassenvererbung oder ein beliebiger __getattr__()-Hook im Spiel waren, konnte dies immer noch ungenau sein.
Die eine große Idee, die dem neuen Klassenmodell zugrunde liegt, ist die Formalisierung einer API zur Beschreibung der Attribute eines Objekts mithilfe von Deskriptoren. Deskriptoren geben den Wert eines Attributs an und geben an, ob es sich um eine Methode oder ein Feld handelt. Mit der Deskriptor-API werden statische und Klassenmethoden ebenso möglich wie exotischere Konstrukte.
Attribut-Deskriptoren sind Objekte, die sich innerhalb von Klassenobjekten befinden und einige eigene Attribute haben.
__name__ist der Name des Attributs.__doc__ist der Docstring des Attributs.__get__(object)ist eine Methode, die den Attributwert von object abruft.__set__(object, value)setzt das Attribut auf object auf value.__delete__(object, value)löscht das Attribut value von object.
Wenn Sie beispielsweise obj.x schreiben, sind die Schritte, die Python tatsächlich ausführt:
descriptor = obj.__class__.x
descriptor.__get__(obj)
Für Methoden gibt descriptor.__get__ ein temporäres Objekt zurück, das aufrufbar ist und die Instanz und die Methode, die auf ihr aufgerufen werden soll, umschließt. Dies ist auch der Grund, warum statische und Klassenmethoden jetzt möglich sind; sie haben Deskriptoren, die nur die Methode oder die Methode und die Klasse umschließen. Als kurze Erklärung dieser neuen Arten von Methoden: Statische Methoden erhalten die Instanz nicht und ähneln daher regulären Funktionen. Klassenmethoden erhalten die Klasse des Objekts, aber nicht das Objekt selbst. Statische und Klassenmethoden werden wie folgt definiert:
class C(object):
def f(arg1, arg2):
...
f = staticmethod(f)
def g(cls, arg1, arg2):
...
g = classmethod(g)
Die Funktion staticmethod() nimmt die Funktion f() und gibt sie, verpackt in einem Deskriptor, zurück, damit sie im Klassenobjekt gespeichert werden kann. Sie könnten erwarten, dass es eine spezielle Syntax zum Erstellen solcher Methoden gibt (z. B. def static f, defstatic f() oder etwas Ähnliches), aber eine solche Syntax wurde noch nicht definiert; das wurde zukünftigen Versionen von Python überlassen.
Weitere neue Funktionen wie Slots und Properties werden ebenfalls als neue Arten von Deskriptoren implementiert, und es ist nicht schwierig, eine Deskriptor-Klasse zu schreiben, die etwas Neues tut. Zum Beispiel wäre es möglich, eine Deskriptor-Klasse zu schreiben, die die Erstellung von Eiffel-ähnlichen Vor- und Nachbedingungen für eine Methode ermöglicht. Eine Klasse, die diese Funktion verwendet, könnte wie folgt definiert werden:
from eiffel import eiffelmethod
class C(object):
def f(self, arg1, arg2):
# The actual function
...
def pre_f(self):
# Check preconditions
...
def post_f(self):
# Check postconditions
...
f = eiffelmethod(f, pre_f, post_f)
Beachten Sie, dass eine Person, die die neue eiffelmethod()-Funktion verwendet, nichts über Deskriptoren verstehen muss. Deshalb denke ich, dass die neuen Funktionen die grundlegende Komplexität der Sprache nicht erhöhen. Es wird einige wenige „Zauberer“ geben, die sie kennen müssen, um eiffelmethod() oder ZODB oder was auch immer zu schreiben, aber die meisten Benutzer werden einfach Code auf Basis der resultierenden Bibliotheken schreiben und die Implementierungsdetails ignorieren.
Mehrfachvererbung: Die Diamantregel¶
Mehrfachvererbung wurde auch durch die Änderung der Regeln für die Namensauflösung nützlicher gemacht. Betrachten Sie diese Klassensammlung (Diagramm entnommen aus PEP 253 von Guido van Rossum)
class A:
^ ^ def save(self): ...
/ \
/ \
/ \
/ \
class B class C:
^ ^ def save(self): ...
\ /
\ /
\ /
\ /
class D
Die Suchregel für klassische Klassen ist einfach, aber nicht sehr intelligent; die Basisklassen werden tiefenorientiert durchsucht, von links nach rechts. Eine Referenz auf D.save() durchsucht die Klassen D, B und dann A, wo save() gefunden und zurückgegeben wird. C.save() würde nie gefunden werden. Das ist schlecht, denn wenn die save()-Methode von C einen internen Zustand speichert, der spezifisch für C ist, führt das Nichtaufrufen dazu, dass dieser Zustand nie gespeichert wird.
Neuartige Klassen folgen einem anderen Algorithmus, der etwas komplizierter zu erklären ist, aber in dieser Situation das Richtige tut. (Beachten Sie, dass Python 2.3 diesen Algorithmus in einen ändert, der in den meisten Fällen die gleichen Ergebnisse liefert, aber für wirklich komplizierte Vererbungsgraphen nützlichere Ergebnisse liefert.)
Listen Sie alle Basisklassen auf, folgen Sie der klassischen Suchregel und schließen Sie eine Klasse mehrmals ein, wenn sie wiederholt besucht wird. Im obigen Beispiel ist die Liste der besuchten Klassen [
D,B,A,C,A].Durchsuchen Sie die Liste nach doppelten Klassen. Wenn welche gefunden werden, entfernen Sie alle bis auf eine, wobei die *letzte* in der Liste verbleibt. Im obigen Beispiel wird die Liste nach dem Entfernen von Duplikaten zu [
D,B,C,A].
Nach dieser Regel gibt die Referenz auf D.save() C.save() zurück, was das gewünschte Verhalten ist. Diese Suchregel ist dieselbe wie die von Common Lisp. Eine neue integrierte Funktion, super(), bietet eine Möglichkeit, auf die Superklassen einer Klasse zuzugreifen, ohne Pythons Algorithmus neu implementieren zu müssen. Die am häufigsten verwendete Form ist super(class, obj), die ein gebundenes Superklassenobjekt (nicht das eigentliche Klassenobjekt) zurückgibt. Diese Form wird in Methoden verwendet, um eine Methode in der Superklasse aufzurufen; zum Beispiel würde die save()-Methode von D wie folgt aussehen:
class D (B,C):
def save (self):
# Call superclass .save()
super(D, self).save()
# Save D's private information here
...
super() kann auch ungebundene Superklassenobjekte zurückgeben, wenn es als super(class) oder super(class1, class2) aufgerufen wird, aber das wird wahrscheinlich nicht oft nützlich sein.
Attributzugriff¶
Eine ganze Reihe ausgeklügelter Python-Klassen definieren Hooks für den Attributzugriff mit __getattr__(); am häufigsten geschieht dies aus Bequemlichkeitsgründen, um Code lesbarer zu machen, indem ein Attributzugriff wie obj.parent automatisch in einen Methodenaufruf wie obj.get_parent abgebildet wird. Python 2.2 fügt einige neue Möglichkeiten zur Steuerung des Attributzugriffs hinzu.
Erstens wird __getattr__(attr_name) von neuartigen Klassen weiterhin unterstützt, und daran hat sich nichts geändert. Wie zuvor wird es aufgerufen, wenn versucht wird, auf obj.foo zuzugreifen und kein Attribut namens foo im Wörterbuch der Instanz gefunden wird.
Neuartige Klassen unterstützen auch eine neue Methode, __getattribute__(attr_name). Der Unterschied zwischen den beiden Methoden besteht darin, dass __getattribute__() *immer* aufgerufen wird, wenn auf ein Attribut zugegriffen wird, während das alte __getattr__() nur aufgerufen wird, wenn foo nicht im Wörterbuch der Instanz gefunden wird.
Die Unterstützung von Python 2.2 für Properties wird jedoch oft ein einfacherer Weg sein, Attributreferenzen abzufangen. Das Schreiben einer __getattr__()-Methode ist kompliziert, da Sie darin keine regulären Attributzugriffe verwenden können, um Rekursion zu vermeiden, und stattdessen mit dem Inhalt von __dict__ herumfummeln müssen. __getattr__()-Methoden werden auch von Python aufgerufen, wenn es nach anderen Methoden wie __repr__() oder __coerce__() sucht, und müssen daher entsprechend geschrieben werden. Schließlich führt der Aufruf einer Funktion bei jedem Attributzugriff zu einem erheblichen Performanceverlust.
property ist ein neuer integrierter Typ, der drei Funktionen zum Abrufen, Setzen oder Löschen eines Attributs sowie einen Docstring zusammenfasst. Wenn Sie beispielsweise ein size-Attribut definieren möchten, das berechnet wird, aber auch gesetzt werden kann, könnten Sie schreiben:
class C(object):
def get_size (self):
result = ... computation ...
return result
def set_size (self, size):
... compute something based on the size
and set internal state appropriately ...
# Define a property. The 'delete this attribute'
# method is defined as None, so the attribute
# can't be deleted.
size = property(get_size, set_size,
None,
"Storage size of this instance")
Das ist sicherlich klarer und einfacher zu schreiben als ein Paar __getattr__()/__setattr__()-Methoden, die nach dem size-Attribut suchen und es speziell behandeln, während alle anderen Attribute aus dem __dict__ der Instanz abgerufen werden. Zugriffe auf size sind auch die einzigen, die die Arbeit des Funktionsaufrufs ausführen müssen, sodass Referenzen auf andere Attribute mit ihrer üblichen Geschwindigkeit laufen.
Schließlich ist es möglich, die Liste der Attribute, auf die auf einem Objekt zugegriffen werden kann, mit dem neuen Klassenattribut __slots__ einzuschränken. Python-Objekte sind normalerweise sehr dynamisch; jederzeit ist es möglich, ein neues Attribut auf einer Instanz zu definieren, indem man einfach obj.new_attr=1 ausführt. Eine neuartige Klasse kann ein Klassenattribut namens __slots__ definieren, um die legalen Attribute auf eine bestimmte Menge von Namen zu beschränken. Ein Beispiel wird dies verdeutlichen:
>>> class C(object):
... __slots__ = ('template', 'name')
...
>>> obj = C()
>>> print obj.template
None
>>> obj.template = 'Test'
>>> print obj.template
Test
>>> obj.newattr = None
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'newattr'
Beachten Sie, wie Sie einen AttributeError erhalten, wenn Sie versuchen, einem Attribut zuzuweisen, das nicht in __slots__ aufgeführt ist.
PEP 234: Iteratoren¶
Eine weitere bedeutende Ergänzung zu 2.2 ist eine Iterationsschnittstelle auf C- und Python-Ebene. Objekte können definieren, wie sie von Aufrufern durchlaufen werden können.
In Python-Versionen bis 2.1 ist der übliche Weg, for item in obj zum Laufen zu bringen, die Definition einer __getitem__()-Methode, die in etwa so aussieht:
def __getitem__(self, index):
return <next item>
__getitem__() ist richtigerweise zur Definition einer Indexoperation auf einem Objekt gedacht, sodass Sie obj[5] schreiben können, um das sechste Element abzurufen. Es ist etwas irreführend, wenn Sie dies nur zur Unterstützung von for-Schleifen verwenden. Betrachten Sie ein dateiähnliches Objekt, das durchlaufen werden soll; der *Index*-Parameter ist im Grunde bedeutungslos, da die Klasse davon ausgeht, dass eine Reihe von __getitem__()-Aufrufen mit inkrementierendem *Index* von Eins gemacht werden. Mit anderen Worten, die Existenz der __getitem__()-Methode bedeutet nicht, dass die Verwendung von file[5] für den zufälligen Zugriff auf das sechste Element funktioniert, obwohl sie es wirklich sollte.
In Python 2.2 kann die Iteration separat implementiert werden, und __getitem__()-Methoden können auf Klassen beschränkt werden, die tatsächlich zufälligen Zugriff unterstützen. Die Grundidee von Iteratoren ist einfach. Eine neue integrierte Funktion, iter(obj) oder iter(C, sentinel), wird verwendet, um einen Iterator zu erhalten. iter(obj) gibt einen Iterator für das Objekt *obj* zurück, während iter(C, sentinel) einen Iterator zurückgibt, der das aufrufbare Objekt *C* aufruft, bis es *sentinel* zurückgibt, um zu signalisieren, dass der Iterator beendet ist.
Python-Klassen können eine Methode __iter__() definieren, die einen neuen Iterator für das Objekt erstellen und zurückgeben soll; wenn das Objekt selbst sein Iterator ist, kann diese Methode einfach self zurückgeben. Insbesondere sind Iteratoren normalerweise ihre eigenen Iteratoren. In C implementierte Erweiterungstypen können eine Funktion tp_iter implementieren, um einen Iterator zurückzugeben, und Erweiterungstypen, die sich als Iteratoren verhalten sollen, können eine Funktion tp_iternext definieren.
Was machen Iteratoren also eigentlich? Sie haben eine erforderliche Methode, next(), die keine Argumente entgegennimmt und den nächsten Wert zurückgibt. Wenn keine Werte mehr zurückgegeben werden können, sollte das Aufrufen von next() die Ausnahme StopIteration auslösen.
>>> L = [1,2,3]
>>> i = iter(L)
>>> print i
<iterator object at 0x8116870>
>>> i.next()
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
>>>
In 2.2 erwartet die -Anweisung von Python keine Sequenz mehr; sie erwartet etwas, für das foriter() einen Iterator zurückgibt. Zur Abwärtskompatibilität und Bequemlichkeit wird für Sequenzen, die weder __iter__() noch einen tp_iter-Slot implementieren, automatisch ein Iterator konstruiert, sodass for i in [1,2,3] weiterhin funktioniert. Überall dort, wo der Python-Interpreter über eine Sequenz iteriert, wurde dies geändert, um das Iterator-Protokoll zu verwenden. Das bedeutet, Sie können Dinge wie diese tun
>>> L = [1,2,3]
>>> i = iter(L)
>>> a,b,c = i
>>> a,b,c
(1, 2, 3)
Die Iterator-Unterstützung wurde zu einigen grundlegenden Typen von Python hinzugefügt. Das Aufrufen von iter() auf einem Dictionary gibt einen Iterator zurück, der über seine Schlüssel iteriert
>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
... 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
>>> for key in m: print key, m[key]
...
Mar 3
Feb 2
Aug 8
Sep 9
May 5
Jun 6
Jul 7
Jan 1
Apr 4
Nov 11
Dec 12
Oct 10
Das ist nur das Standardverhalten. Wenn Sie über Schlüssel, Werte oder Schlüssel/Wert-Paare iterieren möchten, können Sie explizit die Methoden iterkeys(), itervalues() oder iteritems() aufrufen, um einen entsprechenden Iterator zu erhalten. In einer geringfügig verwandten Änderung funktioniert der Operator in jetzt auf Dictionaries, sodass key in dict jetzt äquivalent zu dict.has_key(key) ist.
Dateien bieten ebenfalls einen Iterator, der die Methode readline() aufruft, bis keine Zeilen mehr in der Datei vorhanden sind. Das bedeutet, Sie können jetzt jede Zeile einer Datei mit Code wie diesem lesen
for line in file:
# do something for each line
...
Beachten Sie, dass Sie in einem Iterator nur vorwärts gehen können; es gibt keine Möglichkeit, zum vorherigen Element zurückzukehren, den Iterator zurückzusetzen oder eine Kopie davon zu erstellen. Ein Iterator-Objekt könnte solche zusätzlichen Fähigkeiten bieten, aber das Iterator-Protokoll erfordert lediglich eine next()-Methode.
Siehe auch
- PEP 234 - Iteratoren
Geschrieben von Ka-Ping Yee und GvR; implementiert von der Python Labs Crew, hauptsächlich von GvR und Tim Peters.
PEP 255: Simple Generators¶
Generatoren sind ein weiteres neues Feature, das mit der Einführung von Iteratoren interagiert.
Sie sind zweifellos damit vertraut, wie Funktionsaufrufe in Python oder C funktionieren. Wenn Sie eine Funktion aufrufen, erhält sie einen privaten Namensraum, in dem ihre lokalen Variablen erstellt werden. Wenn die Funktion eine return-Anweisung erreicht, werden die lokalen Variablen zerstört und der resultierende Wert wird an den Aufrufer zurückgegeben. Ein späterer Aufruf derselben Funktion erhält einen frischen Satz lokaler Variablen. Aber was wäre, wenn die lokalen Variablen beim Verlassen einer Funktion nicht weggeworfen würden? Was wäre, wenn Sie die Funktion später dort fortsetzen könnten, wo Sie aufgehört haben? Dies ist es, was Generatoren bieten; sie können als fortsetzbare Funktionen betrachtet werden.
Hier ist das einfachste Beispiel einer Generatorfunktion
def generate_ints(N):
for i in range(N):
yield i
Ein neues Schlüsselwort, yield, wurde für Generatoren eingeführt. Jede Funktion, die eine yield-Anweisung enthält, ist eine Generatorfunktion; dies wird vom Bytecode-Compiler von Python erkannt, der die Funktion entsprechend speziell kompiliert. Da ein neues Schlüsselwort eingeführt wurde, müssen Generatoren in einem Modul explizit aktiviert werden, indem eine Anweisung from __future__ import generators am Anfang des Quellcodes des Moduls enthalten ist. In Python 2.3 wird diese Anweisung unnötig.
Wenn Sie eine Generatorfunktion aufrufen, gibt sie keinen einzelnen Wert zurück; stattdessen gibt sie ein Generatorobjekt zurück, das das Iterator-Protokoll unterstützt. Bei der Ausführung der yield-Anweisung gibt der Generator den Wert von i aus, ähnlich einer return-Anweisung. Der große Unterschied zwischen yield und einer return-Anweisung besteht darin, dass der Ausführungszustand des Generators beim Erreichen eines yield unterbrochen wird und lokale Variablen erhalten bleiben. Beim nächsten Aufruf der next()-Methode des Generators wird die Funktion unmittelbar nach der yield-Anweisung fortgesetzt. (Aus komplizierten Gründen ist die yield-Anweisung innerhalb des try-Blocks einer try…finally-Anweisung nicht erlaubt; lesen Sie PEP 255 für eine vollständige Erklärung der Interaktion zwischen yield und Ausnahmen.)
Hier ist eine Beispielverwendung des Generators generate_ints()
>>> gen = generate_ints(3)
>>> gen
<generator object at 0x8117f90>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in generate_ints
StopIteration
Sie könnten ebenso for i in generate_ints(5) oder a,b,c = generate_ints(3) schreiben.
Innerhalb einer Generatorfunktion kann die return-Anweisung nur ohne Wert verwendet werden und signalisiert das Ende der Wertefolge; danach kann der Generator keine weiteren Werte mehr zurückgeben. return mit einem Wert, wie z. B. return 5, ist eine Syntaxfehlermeldung innerhalb einer Generatorfunktion. Das Ende der Ergebnisse des Generators kann auch durch manuelles Auslösen von StopIteration oder einfach dadurch signalisiert werden, dass der Ausführungsfluss vom Ende der Funktion abfällt.
Sie könnten die Wirkung von Generatoren manuell erzielen, indem Sie Ihre eigene Klasse schreiben und alle lokalen Variablen des Generators als Instanzvariablen speichern. Zum Beispiel könnte die Rückgabe einer Liste von Ganzzahlen durch Setzen von self.count auf 0 und die next()-Methode, die self.count inkrementiert und zurückgibt, erreicht werden. Für einen mäßig komplizierten Generator wäre das Schreiben einer entsprechenden Klasse jedoch viel unübersichtlicher. Lib/test/test_generators.py enthält eine Reihe interessanterer Beispiele. Das einfachste implementiert eine In-Order-Traversal eines Baumes unter rekursiver Verwendung von Generatoren.
# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
Zwei weitere Beispiele in Lib/test/test_generators.py liefern Lösungen für das N-Damen-Problem (Platzieren von $N$ Damen auf einem $NxN$-Schachbrett, sodass keine Dame eine andere bedroht) und die Springer-Tour (eine Route, die einen Springer zu jedem Feld eines $NxN$-Schachbretts führt, ohne ein Feld zweimal zu besuchen).
Die Idee der Generatoren stammt aus anderen Programmiersprachen, insbesondere aus Icon (https://www2.cs.arizona.edu/icon/), wo die Idee der Generatoren zentral ist. In Icon verhält sich jeder Ausdruck und Funktionsaufruf wie ein Generator. Ein Beispiel aus „An Overview of the Icon Programming Language“ unter https://www2.cs.arizona.edu/icon/docs/ipd266.htm gibt eine Vorstellung davon, wie das aussieht
sentence := "Store it in the neighboring harbor"
if (i := find("or", sentence)) > 5 then write(i)
In Icon gibt die Funktion find() die Indizes zurück, an denen der Teilstring „or“ gefunden wird: 3, 23, 33. In der if-Anweisung wird i zuerst der Wert 3 zugewiesen, aber 3 ist kleiner als 5, also schlägt der Vergleich fehl, und Icon versucht es erneut mit dem zweiten Wert von 23. 23 ist größer als 5, also gelingt der Vergleich jetzt, und der Code gibt den Wert 23 auf dem Bildschirm aus.
Python geht bei der Übernahme von Generatoren als zentrales Konzept nicht annähernd so weit wie Icon. Generatoren gelten als neuer Teil der Kernsprache Python, aber ihr Erlernen oder Benutzen ist nicht zwingend erforderlich; wenn sie keine Probleme lösen, die Sie haben, können Sie sie gerne ignorieren. Ein neuartiges Merkmal der Python-Schnittstelle im Vergleich zu Icon ist, dass der Zustand eines Generators als konkretes Objekt (der Iterator) dargestellt wird, das an andere Funktionen übergeben oder in einer Datenstruktur gespeichert werden kann.
Siehe auch
- PEP 255 - Simple Generators
Geschrieben von Neil Schemenauer, Tim Peters, Magnus Lie Hetland. Implementiert hauptsächlich von Neil Schemenauer und Tim Peters, mit anderen Korrekturen von der Python Labs Crew.
PEP 237: Unifying Long Integers and Integers¶
In neueren Versionen wurde die Unterscheidung zwischen normalen Ganzzahlen, die auf den meisten Maschinen 32-Bit-Werte sind, und langen Ganzzahlen, die eine beliebige Größe haben können, zu einem Ärgernis. Zum Beispiel muss auf Plattformen, die Dateien größer als 2**32 Bytes unterstützen, die Methode tell() von Datei-Objekten eine lange Ganzzahl zurückgeben. Es gab jedoch verschiedene Teile von Python, die einfache Ganzzahlen erwarteten und einen Fehler auslösten, wenn stattdessen eine lange Ganzzahl bereitgestellt wurde. Zum Beispiel konnten in Python 1.5 nur normale Ganzzahlen als Slice-Index verwendet werden, und 'abc'[1L:] würde eine TypeError-Ausnahme mit der Meldung „slice index must be int“ auslösen.
Python 2.2 wird Werte nach Bedarf von kurzen zu langen Ganzzahlen verschieben. Das „L“-Suffix ist nicht mehr erforderlich, um eine lange Ganzzahl-Literal anzugeben, da der Compiler nun den geeigneten Typ wählt. (Die Verwendung des „L“-Suffix wird in zukünftigen 2.x-Versionen von Python abgeraten, in Python 2.4 eine Warnung auslösen und wahrscheinlich in Python 3.0 entfernt werden.) Viele Operationen, die bisher eine OverflowError auslösten, geben nun eine lange Ganzzahl als Ergebnis zurück. Zum Beispiel
>>> 1234567890123
1234567890123L
>>> 2 ** 64
18446744073709551616L
In den meisten Fällen werden Ganzzahlen und lange Ganzzahlen nun identisch behandelt. Sie können sie immer noch mit der eingebauten Funktion type() unterscheiden, aber das ist selten nötig.
Siehe auch
- PEP 237 - Unifying Long Integers and Integers
Geschrieben von Moshe Zadka und Guido van Rossum. Implementiert hauptsächlich von Guido van Rossum.
PEP 238: Changing the Division Operator¶
Die umstrittenste Änderung in Python 2.2 leitet die Bemühungen zur Behebung eines alten Designfehlers ein, der Python von Anfang an begleitet hat. Derzeit verhält sich der Divisionsoperator / von Python bei zwei Ganzzahl-Argumenten wie der Divisionsoperator von C: Er gibt ein ganzzahliges Ergebnis zurück, das bei einem Bruchteil nach unten abgeschnitten wird. Zum Beispiel ist 3/2 1 und nicht 1,5, und (-1)/2 ist -1 und nicht -0,5. Das bedeutet, dass die Ergebnisse der Division je nach Typ der beiden Operanden unerwartet variieren können, und da Python dynamisch typisiert ist, kann es schwierig sein, die möglichen Typen der Operanden zu bestimmen.
(Die Kontroverse dreht sich darum, ob dies *wirklich* ein Designfehler ist und ob es sich lohnt, bestehenden Code zu brechen, um ihn zu beheben. Sie hat endlose Diskussionen auf python-dev ausgelöst und im Juli 2001 in einem Sturm säuerlich sarkastischer Beiträge auf comp.lang.python gipfelte. Ich werde hier keine der beiden Seiten verteidigen und mich darauf beschränken, zu beschreiben, was in 2.2 implementiert ist. Lesen Sie PEP 238 für eine Zusammenfassung der Argumente und Gegenargumente.)
Da diese Änderung Code brechen könnte, wird sie sehr schrittweise eingeführt. Python 2.2 beginnt die Umstellung, aber die Umstellung wird erst in Python 3.0 abgeschlossen sein.
Zuerst leihe ich mir einige Terminologien aus PEP 238. „True Division“ (echte Division) ist die Division, die die meisten Nicht-Programmierer kennen: 3/2 ist 1,5, 1/4 ist 0,25 und so weiter. „Floor Division“ (Ganzzahldivision) ist das, was der Operator / von Python tut, wenn er Ganzzahl-Operanden erhält; das Ergebnis ist der Bodenwert des von der echten Division zurückgegebenen Wertes. „Classic Division“ (klassische Division) ist das aktuelle gemischte Verhalten von /; es gibt das Ergebnis der Ganzzahldivision zurück, wenn die Operanden Ganzzahlen sind, und das Ergebnis der echten Division zurück, wenn einer der Operanden eine Gleitkommazahl ist.
Hier sind die Änderungen, die 2.2 einführt
Ein neuer Operator,
//, ist der Ganzzahldivisionsoperator. (Ja, wir wissen, dass er wie das Kommentarzeichen von C++ aussieht.)//führt *immer* eine Ganzzahldivision durch, unabhängig von den Typen seiner Operanden, sodass1 // 20 ist und1.0 // 2.0ebenfalls 0.0 ist.//ist in Python 2.2 immer verfügbar; Sie müssen es nicht mit einer__future__-Anweisung aktivieren.Durch das Hinzufügen von
from __future__ import divisionin einem Modul wird der Operator/so geändert, dass er das Ergebnis der echten Division zurückgibt, sodass1/20,5 ist. Ohne die__future__-Anweisung bedeutet/immer noch klassische Division. Die Standardbedeutung von/ändert sich erst in Python 3.0.Klassen können Methoden namens
__truediv__()und__floordiv__()definieren, um die beiden Divisionsoperatoren zu überladen. Auf C-Ebene gibt es auch Slots in der StrukturPyNumberMethods, sodass Erweiterungstypen die beiden Operatoren definieren können.Python 2.2 unterstützt einige Kommandozeilenargumente, um zu testen, ob Code mit den geänderten Divisionssemantiken funktioniert. Wenn Sie python mit
-Q warnausführen, wird bei jeder Division von zwei Ganzzahlen eine Warnung ausgegeben. Sie können dies verwenden, um Code zu finden, der von der Änderung betroffen ist, und ihn zu beheben. Standardmäßig führt Python 2.2 einfach klassische Division ohne Warnung durch; die Warnung wird in Python 2.3 standardmäßig aktiviert.
Siehe auch
- PEP 238 - Changing the Division Operator
Geschrieben von Moshe Zadka und Guido van Rossum. Implementiert von Guido van Rossum.
Unicode Changes¶
Die Unicode-Unterstützung von Python wurde in 2.2 etwas verbessert. Unicode-Zeichenketten werden normalerweise als UCS-2 gespeichert, als 16-Bit-Ganzzahlen ohne Vorzeichen. Python 2.2 kann auch mit UCS-4, 32-Bit-Ganzzahlen ohne Vorzeichen, als interne Kodierung kompiliert werden, indem --enable-unicode=ucs4 an das Konfigurationsskript übergeben wird. (Es ist auch möglich, --disable-unicode anzugeben, um die Unicode-Unterstützung vollständig zu deaktivieren.)
Beim Erstellen für UCS-4 (ein „breites Python“) kann der Interpreter Unicode-Zeichen von U+000000 bis U+110000 nativ verarbeiten, sodass der Bereich der zulässigen Werte für die Funktion unichr() entsprechend erweitert wird. Bei Verwendung eines Interpreters, der für UCS-2 (ein „schmales Python“) kompiliert wurde, lösen Werte größer als 65535 immer noch eine ValueError-Ausnahme aus, wenn unichr() aufgerufen wird. All dies wird in PEP 261, „Support for ‘wide’ Unicode characters“, beschrieben; konsultieren Sie diese für weitere Details.
Eine weitere Änderung ist einfacher zu erklären. Seit ihrer Einführung unterstützen Unicode-Zeichenketten eine Methode encode(), um die Zeichenkette in eine ausgewählte Kodierung wie UTF-8 oder Latin-1 zu konvertieren. Eine symmetrische Methode decode([*encoding*]) wurde in 2.2 zu 8-Bit-Zeichenketten hinzugefügt (aber nicht zu Unicode-Zeichenketten). decode() geht davon aus, dass die Zeichenkette in der angegebenen Kodierung vorliegt, und dekodiert sie, wobei das zurückgegeben wird, was vom Codec zurückgegeben wird.
Mit dieser neuen Funktion wurden Codecs für Aufgaben hinzugefügt, die nicht direkt mit Unicode zusammenhängen. Zum Beispiel wurden Codecs für uu-Encoding, das Base64-Encoding von MIME und die Komprimierung mit dem Modul zlib hinzugefügt
>>> s = """Here is a lengthy piece of redundant, overly verbose,
... and repetitive text.
... """
>>> data = s.encode('zlib')
>>> data
'x\x9c\r\xc9\xc1\r\x80 \x10\x04\xc0?Ul...'
>>> data.decode('zlib')
'Here is a lengthy piece of redundant, overly verbose,\nand repetitive text.\n'
>>> print s.encode('uu')
begin 666 <data>
M2&5R92!I<R!A(&QE;F=T:'D@<&EE8V4@;V8@<F5D=6YD86YT+"!O=F5R;'D@
>=F5R8F]S92P*86YD(')E<&5T:71I=F4@=&5X="X*
end
>>> "sheesh".encode('rot-13')
'furrfu'
Um eine Klasseninstanz in Unicode zu konvertieren, kann eine Klasse eine Methode __unicode__() definieren, analog zu __str__().
encode(), decode() und __unicode__() wurden von Marc-André Lemburg implementiert. Die Änderungen zur Unterstützung der internen Verwendung von UCS-4 wurden von Fredrik Lundh und Martin von Löwis implementiert.
Siehe auch
- PEP 261 - Support for ‘wide’ Unicode characters
Geschrieben von Paul Prescod.
PEP 227: Nested Scopes¶
In Python 2.1 wurden statisch verschachtelte Bereiche (nested scopes) als optionale Funktion hinzugefügt, die durch eine Direktive from __future__ import nested_scopes aktiviert werden musste. In 2.2 müssen verschachtelte Bereiche nicht mehr speziell aktiviert werden und sind jetzt immer vorhanden. Der Rest dieses Abschnitts ist eine Kopie der Beschreibung von verschachtelten Bereichen aus meinem Dokument „What’s New in Python 2.1“; wenn Sie es gelesen haben, als 2.1 herauskam, können Sie den Rest dieses Abschnitts überspringen.
Die größte Änderung, die in Python 2.1 eingeführt und in 2.2 abgeschlossen wurde, betrifft die Scoping-Regeln von Python. In Python 2.0 gibt es zu jedem Zeitpunkt höchstens drei Namensräume, die zur Suche nach Variablennamen verwendet werden: lokal, Modul-Ebene und der eingebaute Namensraum. Dies überraschte die Leute oft, weil es ihren intuitiven Erwartungen nicht entsprach. Zum Beispiel funktioniert eine verschachtelte rekursive Funktionsdefinition nicht
def f():
...
def g(value):
...
return g(value-1) + 1
...
Die Funktion g() wird immer eine NameError-Ausnahme auslösen, da die Bindung des Namens g weder in ihrem lokalen Namensraum noch im Modul-Namensraum vorhanden ist. Dies ist in der Praxis kein großes Problem (wie oft definiert man innere Funktionen wie diese rekursiv?), aber es machte auch die Verwendung des Ausdrucks lambda umständlicher, und das war ein praktisches Problem. In Code, der lambda verwendet, finden Sie oft lokale Variablen, die durch Übergabe als Standardwerte von Argumenten kopiert werden.
def find(self, name):
"Return list of any entries equal to 'name'"
L = filter(lambda x, name=name: x == name,
self.list_attribute)
return L
Die Lesbarkeit von Python-Code, der in einem stark funktionalen Stil geschrieben ist, leidet erheblich darunter.
Die bedeutendste Änderung in Python 2.2 ist die Einführung der statischen Geltungsbereiche (static scoping) in die Sprache, um dieses Problem zu beheben. Als erste Auswirkung ist das Standardargument name=name im obigen Beispiel nicht mehr notwendig. Vereinfacht gesagt: Wenn einer Variablen innerhalb einer Funktion kein Wert zugewiesen wird (durch eine Zuweisung oder die Anweisungen def, class oder import), werden Referenzen auf die Variable im lokalen Namensraum des umschließenden Bereichs nachgeschlagen. Eine detailliertere Erklärung der Regeln und eine Zerlegung der Implementierung finden Sie in der PEP.
Diese Änderung kann zu einigen Kompatibilitätsproblemen für Code führen, bei dem derselbe Variablenname sowohl auf Modul-Ebene als auch als lokale Variable innerhalb einer Funktion verwendet wird, die weitere Funktionsdefinitionen enthält. Dies erscheint jedoch eher unwahrscheinlich, da solcher Code ohnehin ziemlich verwirrend zu lesen gewesen wäre.
Eine Nebenwirkung der Änderung ist, dass die Anweisungen from module import * und exec innerhalb eines Funktionsbereichs unter bestimmten Bedingungen illegal geworden sind. Das Python-Referenzhandbuch hat schon immer besagt, dass from module import * nur auf der obersten Ebene eines Moduls zulässig ist, aber der CPython-Interpreter hat dies bisher nie durchgesetzt. Im Rahmen der Implementierung von verschachtelten Bereichen muss der Compiler, der Python-Quellcode in Bytecodes umwandelt, unterschiedlichen Code generieren, um auf Variablen in einem enthaltenden Bereich zuzugreifen. from module import * und exec machen es dem Compiler unmöglich, dies herauszufinden, da sie dem lokalen Namensraum Namen hinzufügen, die zur Kompilierungszeit unbekannt sind. Daher wird der Compiler, wenn eine Funktion Funktionsdefinitionen oder lambda-Ausdrücke mit freien Variablen enthält, dies durch Auslösen einer SyntaxError-Ausnahme signalisieren.
Um die vorherige Erklärung etwas klarer zu machen, hier ein Beispiel
x = 1
def f():
# The next line is a syntax error
exec 'x=2'
def g():
return x
Zeile 4 mit der exec-Anweisung ist ein Syntaxfehler, da exec eine neue lokale Variable namens x definieren würde, deren Wert von g() abgerufen werden sollte.
Dies sollte keine große Einschränkung sein, da exec in den meisten Python-Codes selten verwendet wird (und wenn es verwendet wird, ist es oft sowieso ein Zeichen für schlechtes Design).
Siehe auch
- PEP 227 - Statically Nested Scopes
Geschrieben und implementiert von Jeremy Hylton.
Neue und verbesserte Module¶
Das Modul
xmlrpclibwurde von Fredrik Lundh zur Standardbibliothek beigetragen und bietet Unterstützung für das Schreiben von XML-RPC-Clients. XML-RPC ist ein einfaches Remote-Procedure-Call-Protokoll, das auf HTTP und XML aufbaut. Der folgende Ausschnitt ruft beispielsweise eine Liste von RSS-Kanälen vom O’Reilly Network ab und listet dann die aktuellen Schlagzeilen für einen Kanal aufimport xmlrpclib s = xmlrpclib.Server( 'http://www.oreillynet.com/meerkat/xml-rpc/server.php') channels = s.meerkat.getChannels() # channels is a list of dictionaries, like this: # [{'id': 4, 'title': 'Freshmeat Daily News'} # {'id': 190, 'title': '32Bits Online'}, # {'id': 4549, 'title': '3DGamers'}, ... ] # Get the items for one channel items = s.meerkat.getItems( {'channel': 4} ) # 'items' is another list of dictionaries, like this: # [{'link': 'http://freshmeat.net/releases/52719/', # 'description': 'A utility which converts HTML to XSL FO.', # 'title': 'html2fo 0.3 (Default)'}, ... ]
Das Modul
SimpleXMLRPCServererleichtert die Erstellung einfacher XML-RPC-Server. Weitere Informationen zu XML-RPC finden Sie unter http://xmlrpc.scripting.com/.Das neue Modul
hmacimplementiert den HMAC-Algorithmus, der in RFC 2104 beschrieben ist. (Beigetragen von Gerhard Häring.)Mehrere Funktionen, die ursprünglich lange Tupel zurückgaben, geben nun Pseudo-Sequenzen zurück, die sich weiterhin wie Tupel verhalten, aber auch mnemonische Attribute wie
memberst_mtimeodertm_yearhaben. Zu den erweiterten Funktionen gehörenstat(),fstat(),statvfs()undfstatvfs()im Modulos, sowielocaltime(),gmtime()undstrptime()im Modultime.Um beispielsweise die Größe einer Datei mit den alten Tupeln zu ermitteln, müssten Sie etwas schreiben wie
file_size = os.stat(filename)[stat.ST_SIZE], aber jetzt kann dies klarer geschrieben werden alsfile_size = os.stat(filename).st_size.Der ursprüngliche Patch für dieses Feature wurde von Nick Mathewson beigesteuert.
Der Python-Profiler wurde umfassend überarbeitet und verschiedene Fehler in seiner Ausgabe behoben. (Beigetragen von Fred L. Drake, Jr. und Tim Peters.)
Das Modul
socketkann zur Unterstützung von IPv6 kompiliert werden; geben Sie die Option--enable-ipv6für das Configure-Skript von Python an. (Beigetragen von Jun-ichiro „itojun“ Hagino.)Zwei neue Formatzeichen wurden dem Modul
structfür 64-Bit-Integer auf Plattformen hinzugefügt, die den C-Typ long long unterstützen.qist für einen signierten 64-Bit-Integer undQfür einen unsignierten. Der Wert wird als Python-Long-Integer zurückgegeben. (Beigetragen von Tim Peters.)Im interaktiven Modus des Interpreters gibt es eine neue eingebaute Funktion
help(), die das in Python 2.1 eingeführte Modulpydocverwendet, um interaktive Hilfe bereitzustellen.help(object)zeigt alle verfügbaren Hilfetexte zu object an.help()ohne Argument bringt Sie in ein Online-Hilfedienstprogramm, wo Sie die Namen von Funktionen, Klassen oder Modulen eingeben können, um deren Hilfetexte zu lesen. (Beigetragen von Guido van Rossum, unter Verwendung von Ka-Ping Yees Modulpydoc.)Verschiedene Fehlerbehebungen und Leistungsverbesserungen wurden an der SRE-Engine vorgenommen, die dem Modul
rezugrunde liegt. Zum Beispiel wurden die Funktionenre.sub()undre.split()in C neu geschrieben. Ein weiterer beigesteuerter Patch beschleunigt bestimmte Unicode-Zeichenbereiche um den Faktor zwei, und eine neue Methodefinditer()gibt einen Iterator über alle nicht überlappenden Übereinstimmungen in einer gegebenen Zeichenkette zurück. (SRE wird von Fredrik Lundh gepflegt. Der BIGCHARSET-Patch wurde von Martin von Löwis beigesteuert.)Das Modul
smtplibunterstützt nun RFC 2487, "Secure SMTP over TLS", sodass es nun möglich ist, den SMTP-Verkehr zwischen einem Python-Programm und dem Mail-Transport-Agenten zu verschlüsseln, dem eine Nachricht übergeben wird.smtplibunterstützt auch die SMTP-Authentifizierung. (Beigetragen von Gerhard Häring.)Das Modul
imaplib, gepflegt von Piers Lauder, unterstützt mehrere neue Erweiterungen: die NAMESPACE-Erweiterung, definiert in RFC 2342, SORT, GETACL und SETACL. (Beigetragen von Anthony Baxter und Michel Pelletier.)Die Analyse von E-Mail-Adressen durch das Modul
rfc822entspricht nun RFC 2822, einer Aktualisierung von RFC 822. (Der Name des Moduls wird *nicht* inrfc2822geändert.) Ein neues Paket,email, wurde ebenfalls zum Parsen und Generieren von E-Mail-Nachrichten hinzugefügt. (Beigetragen von Barry Warsaw, basierend auf seiner Arbeit an Mailman.)Das Modul
difflibenthält nun eine neue KlasseDifferzur Erstellung menschenlesbarer Listen von Änderungen (ein "Delta") zwischen zwei Textzeilensequenzen. Es gibt auch zwei Generatorfunktionen,ndiff()undrestore(), die jeweils ein Delta aus zwei Sequenzen oder eine der ursprünglichen Sequenzen aus einem Delta zurückgeben. (Grundlegende Arbeit beigesteuert von David Goodger, aus ndiff.py-Code von Tim Peters, der dann die Generatorisierung vornahm.)Neue Konstanten
ascii_letters,ascii_lowercaseundascii_uppercasewurden zum Modulstringhinzugefügt. Mehrere Module in der Standardbibliothek verwendetenstring.letters, um die Bereiche A-Za-z zu bezeichnen, aber diese Annahme ist falsch, wenn Lokalisierungen verwendet werden, dastring.lettersje nach Satz gültiger Zeichen, die durch die aktuelle Lokalisierung definiert sind, variiert. Die fehlerhaften Module wurden alle korrigiert, um stattdessenascii_letterszu verwenden. (Berichtet von einer unbekannten Person; behoben von Fred L. Drake, Jr.)Das Modul
mimetypeserleichtert nun die Verwendung alternativer MIME-Typ-Datenbanken durch die Hinzufügung einer KlasseMimeTypes, die eine Liste von zu parsierenden Dateinamen annimmt. (Beigetragen von Fred L. Drake, Jr.)Eine Klasse
Timerwurde dem Modulthreadinghinzugefügt, die die Planung einer Aktivität zu einem späteren Zeitpunkt ermöglicht. (Beigetragen von Itamar Shtull-Trauring.)
Änderungen und Fehlerbehebungen am Interpreter¶
Einige der Änderungen betreffen nur Personen, die auf C-Ebene mit dem Python-Interpreter arbeiten, weil sie Python-Erweiterungsmodule schreiben, den Interpreter einbetten oder einfach am Interpreter selbst arbeiten. Wenn Sie nur Python-Code schreiben, werden Sie von den hier beschriebenen Änderungen kaum betroffen sein.
Profiling- und Tracing-Funktionen können nun in C implementiert werden, was mit deutlich höherer Geschwindigkeit als Python-basierte Funktionen arbeiten kann und den Overhead von Profiling und Tracing reduzieren sollte. Dies dürfte für Autoren von Entwicklungsumgebungen für Python von Interesse sein. Zwei neue C-Funktionen wurden zur Python-API hinzugefügt:
PyEval_SetProfile()undPyEval_SetTrace(). Die bestehenden Funktionensys.setprofile()undsys.settrace()existieren weiterhin und wurden einfach geändert, um die neue C-Level-Schnittstelle zu nutzen. (Beigetragen von Fred L. Drake, Jr.)Eine weitere Low-Level-API, die hauptsächlich für Implementierer von Python-Debuggern und Entwicklungswerkzeugen interessant ist, wurde hinzugefügt.
PyInterpreterState_Head()undPyInterpreterState_Next()ermöglichen es einem Aufrufer, alle vorhandenen Interpreterobjekte zu durchlaufen;PyInterpreterState_ThreadHead()undPyThreadState_Next()ermöglichen die Schleifung über alle Thread-Zustände für einen gegebenen Interpreter. (Beigetragen von David Beazley.)Die C-Level-Schnittstelle zum Garbage Collector wurde geändert, um die Erstellung von Erweiterungstypen, die Garbage Collection unterstützen, und das Debugging von Fehlverwendungen der Funktionen zu erleichtern. Verschiedene Funktionen haben leicht unterschiedliche Semantiken, daher mussten eine Reihe von Funktionen umbenannt werden. Erweiterungen, die die alte API verwenden, werden weiterhin kompiliert, aber nicht an der Garbage Collection teilnehmen, daher sollte deren Aktualisierung für 2.2 als ziemlich hochpriorisiert betrachtet werden.
Um ein Erweiterungsmodul auf die neue API zu aktualisieren, führen Sie die folgenden Schritte aus
Benennen Sie
Py_TPFLAGS_GCinPy_TPFLAGS_HAVE_GCum.- Verwenden Sie
PyObject_GC_New()oderPyObject_GC_NewVar()zur Allokation von Objekten und
PyObject_GC_Del()zur Deallokation.
- Verwenden Sie
Benennen Sie
PyObject_GC_Init()inPyObject_GC_Track()undPyObject_GC_Fini()inPyObject_GC_UnTrack()um.Entfernen Sie
PyGC_HEAD_SIZEaus Objektgrößenberechnungen.Entfernen Sie Aufrufe von
PyObject_AS_GC()undPyObject_FROM_GC().Eine neue Formatsequenz
etwurde zuPyArg_ParseTuple()hinzugefügt;etnimmt sowohl einen Parameter als auch einen Kodierungsnamen entgegen und konvertiert den Parameter in die angegebene Kodierung, wenn der Parameter eine Unicode-Zeichenkette ist, oder lässt ihn unverändert, wenn es sich um eine 8-Bit-Zeichenkette handelt, und geht davon aus, dass diese bereits in der gewünschten Kodierung vorliegt. Dies unterscheidet sich von der Formatzeichenkettees, die davon ausgeht, dass 8-Bit-Zeichenketten in Pythons Standard-ASCII-Kodierung vorliegen und sie in die angegebene neue Kodierung konvertiert. (Beigetragen von M.-A. Lemburg, und verwendet für die MBCS-Unterstützung unter Windows, die im folgenden Abschnitt beschrieben wird.)Eine andere Funktion zur Argumentenanalyse,
PyArg_UnpackTuple(), wurde hinzugefügt, die einfacher und vermutlich schneller ist. Anstatt eine Formatzeichenkette anzugeben, gibt der Aufrufer einfach die minimale und maximale Anzahl erwarteter Argumente an und eine Reihe von Zeigern auf PyObject*-Variablen, die mit den Argumentwerten gefüllt werden.Zwei neue Flags
METH_NOARGSundMETH_Osind in Methodendefinitionstabellen verfügbar, um die Implementierung von Methoden ohne Argumente oder mit einem einzelnen untypisierten Argument zu vereinfachen. Das Aufrufen solcher Methoden ist effizienter als das Aufrufen einer entsprechenden Methode, dieMETH_VARARGSverwendet. Außerdem ist der alte StilMETH_OLDARGSzum Schreiben von C-Methoden nun offiziell veraltet.Zwei neue Wrapper-Funktionen,
PyOS_snprintf()undPyOS_vsnprintf(), wurden hinzugefügt, um plattformübergreifende Implementierungen der relativ neuen C-Lib-APIssnprintf()undvsnprintf()bereitzustellen. Im Gegensatz zu den Standardfunktionensprintf()undvsprintf()prüfen die Python-Versionen die Grenzen des verwendeten Puffers, um Pufferüberläufe zu verhindern. (Beigetragen von M.-A. Lemburg.)Die Funktion
_PyTuple_Resize()hat einen ungenutzten Parameter verloren, sodass sie nun 2 statt 3 Parameter annimmt. Das dritte Argument wurde nie verwendet und kann bei der Portierung von Code von früheren Versionen auf Python 2.2 einfach verworfen werden.
Andere Änderungen und Korrekturen¶
Wie üblich gab es eine Reihe weiterer Verbesserungen und Fehlerbehebungen, die im Quellcode verteilt waren. Eine Suche in den CVS-Änderungsprotokollen ergibt, dass zwischen Python 2.1 und 2.2 527 Patches angewendet und 683 Fehler behoben wurden; 2.2.1 wendete 139 Patches an und behob 143 Fehler; 2.2.2 wendete 106 Patches an und behob 82 Fehler. Diese Zahlen sind wahrscheinlich Unterschätzungen.
Einige der bemerkenswertesten Änderungen sind:
Der Code für den MacOS-Port von Python, gepflegt von Jack Jansen, befindet sich nun im Haupt-Python-CVS-Baum, und viele Änderungen wurden vorgenommen, um MacOS X zu unterstützen.
Die wichtigste Änderung ist die Möglichkeit, Python als Framework zu bauen, was durch die Bereitstellung der Option
--enable-frameworkfür das Configure-Skript beim Kompilieren von Python ermöglicht wird. Laut Jack Jansen: "Dies installiert eine in sich geschlossene Python-Installation plus das OS X Framework "Glue" in/Library/Frameworks/Python.framework(oder an einem anderen gewählten Ort). Vorerst gibt es hierfür nur wenig unmittelbaren Nutzen (tatsächlich hat man den Nachteil, dass man seinen PATH ändern muss, um Python finden zu können), aber es ist die Grundlage für die Erstellung einer vollwertigen Python-Anwendung, die Portierung der MacPython-IDE, möglicherweise die Verwendung von Python als standardmäßige OSA-Skriptsprache und vieles mehr."Die meisten MacPython-Toolboxmodule, die Schnittstellen zu MacOS-APIs wie Windowing, QuickTime, Scripting usw. herstellen, wurden nach OS X portiert, aber sie bleiben in
setup.pyauskommentiert. Personen, die mit diesen Modulen experimentieren möchten, können sie manuell auskommentieren.Schlüsselwortargumente, die an eingebaute Funktionen übergeben werden, die sie nicht akzeptieren, führen nun zu einer Ausnahme vom Typ
TypeErrormit der Meldung "function takes no keyword arguments".Weak References, die in Python 2.1 als Erweiterungsmodul hinzugefügt wurden, sind nun Teil des Kerns, da sie in der Implementierung von Klassen des neuen Stils verwendet werden. Die Ausnahme
ReferenceErrorwurde daher aus dem Modulweakrefentfernt und ist nun eine eingebaute Ausnahme.Ein neues Skript,
Tools/scripts/cleanfuture.pyvon Tim Peters, entfernt automatisch veraltete__future__-Anweisungen aus Python-Quellcode.Ein zusätzliches Argument flags wurde der eingebauten Funktion
compile()hinzugefügt, sodass das Verhalten von__future__-Anweisungen nun korrekt in simulierten Shells beobachtet werden kann, wie z.B. in IDLE und anderen Entwicklungsumgebungen. Dies ist in PEP 264 beschrieben. (Beigetragen von Michael Hudson.)Die mit Python 1.6 eingeführte neue Lizenz war nicht GPL-kompatibel. Dies wird durch einige geringfügige Textänderungen an der Lizenz von 2.2 behoben, sodass es wieder legal ist, Python in ein GPL-Programm einzubetten. Beachten Sie, dass Python selbst nicht GPL-lizenziert ist, sondern unter einer Lizenz steht, die im Wesentlichen der BSD-Lizenz entspricht, wie es schon immer war. Die Lizenzänderungen wurden auch auf die Python 2.0.1- und 2.1.1-Releases angewendet.
Wenn unter Windows ein Unicode-Dateiname übergeben wird, konvertiert Python ihn nun in eine MBCS-kodierte Zeichenkette, wie sie von den Microsoft-Dateien-APIs verwendet wird. Da MBCS explizit von den Dateien-APIs verwendet wird, erweist sich Pythons Wahl von ASCII als Standardkodierung als ärgerlich. Unter Unix wird die Zeichensatz-Lokalisierung verwendet, wenn
locale.nl_langinfo(CODESET)verfügbar ist. (Windows-Unterstützung wurde von Mark Hammond mit Unterstützung von Marc-André Lemburg beigesteuert. Unix-Unterstützung wurde von Martin von Löwis hinzugefügt.)Unterstützung für große Dateien ist nun unter Windows aktiviert. (Beigetragen von Tim Peters.)
Das Skript
Tools/scripts/ftpmirror.pyanalysiert nun eine.netrc-Datei, falls vorhanden. (Beigetragen von Mike Romberg.)Einige Funktionen des von der Funktion
xrange()zurückgegebenen Objekts sind nun veraltet und lösen bei Zugriff Warnungen aus; sie werden in Python 2.3 verschwinden. xrange-Objekte versuchten, sich wie vollständige Sequenztypen zu verhalten, indem sie Slicing, Sequenzmultiplikation und den Operatorinunterstützten, aber diese Funktionen wurden selten verwendet und waren daher fehlerhaft. Die Methodetolist()und die Attributestart,stopundstepsind ebenfalls veraltet. Auf C-Ebene ist das vierte Argument der FunktionPyRange_New(),repeat, ebenfalls veraltet.Es gab eine Reihe von Patches für die Dictionary-Implementierung, hauptsächlich um mögliche Core-Dumps zu beheben, wenn ein Dictionary Objekte enthält, die heimlich ihren Hash-Wert änderten oder das Dictionary, in dem sie sich befanden, veränderten. Eine Zeit lang fiel Python-Dev in einen sanften Rhythmus, bei dem Michael Hudson einen Fall fand, der zum Absturz führte, Tim Peters den Fehler behob, Michael einen weiteren Fall fand und so weiter.
Unter Windows kann Python nun dank einer Reihe von Patches von Stephen Hansen mit Borland C kompiliert werden, obwohl das Ergebnis noch nicht vollständig funktionsfähig ist. (Aber das ist Fortschritt...)
Eine weitere Windows-Verbesserung: Wise Solutions bot PythonLabs großzügig die Nutzung ihres InstallerMaster 8.1 Systems an. Frühere Windows-Installer von PythonLabs verwendeten Wise 5.0a, das zu altern begann. (Zusammengestellt von Tim Peters.)
Dateien, die auf
.pywenden, können nun unter Windows importiert werden..pywist eine reine Windows-Erweiterung, die verwendet wird, um anzuzeigen, dass ein Skript mit PYTHONW.EXE anstelle von PYTHON.EXE ausgeführt werden muss, um zu verhindern, dass eine DOS-Konsole zur Anzeige der Ausgabe erscheint. Dieser Patch ermöglicht den Import solcher Skripte, falls sie auch als Module verwendbar sind. (Implementiert von David Bolen.)Auf Plattformen, auf denen Python die C-Funktion
dlopen()zum Laden von Erweiterungsmodulen verwendet, können nun die vondlopen()verwendeten Flags mit den Funktionensys.getdlopenflags()undsys.setdlopenflags()gesetzt werden. (Beigetragen von Bram Stolk.)Die eingebaute Funktion
pow()unterstützt nicht mehr 3 Argumente, wenn Gleitkommazahlen übergeben werden.pow(x, y, z)gibt(x**y) % zzurück, aber dies ist für Gleitkommazahlen nie nützlich, und das Endergebnis variiert unvorhersehbar je nach Plattform. Ein Aufruf wiepow(2.0, 8.0, 7.0)löst nun eine Ausnahme vom TypTypeErroraus.
Danksagungen¶
Der Autor möchte den folgenden Personen für ihre Vorschläge, Korrekturen und Unterstützung bei verschiedenen Entwürfen dieses Artikels danken: Fred Bremmer, Keith Briggs, Andrew Dalke, Fred L. Drake, Jr., Carel Fellinger, David Goodger, Mark Hammond, Stephen Hansen, Michael Hudson, Jack Jansen, Marc-André Lemburg, Martin von Löwis, Fredrik Lundh, Michael McLay, Nick Mathewson, Paul Moore, Gustavo Niemeyer, Don O’Donnell, Joonas Paalasma, Tim Peters, Jens Quade, Tom Reinhardt, Neil Schemenauer, Guido van Rossum, Greg Ward, Edward Welbourne.