Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 3114 – Umbenennung von iterator.next() in iterator.__next__()

Autor:
Ka-Ping Yee <ping at zesty.ca>
Status:
Final
Typ:
Standards Track
Erstellt:
04.03.2007
Python-Version:
3.0
Post-History:


Inhaltsverzeichnis

Zusammenfassung

Das Iterator-Protokoll in Python 2.x besteht aus zwei Methoden: __iter__(), die auf einem iterierbaren Objekt aufgerufen wird, um einen Iterator zu liefern, und next(), die auf einem Iterator-Objekt aufgerufen wird, um das nächste Element in der Sequenz zu liefern. Die Verwendung einer for-Schleife, um über ein iterierbares Objekt zu iterieren, ruft implizit beide dieser Methoden auf. Dieser PEP schlägt vor, dass die next-Methode in __next__ umbenannt wird, konsistent mit allen anderen Protokollen in Python, bei denen eine Methode implizit als Teil eines sprachlichen Protokolls aufgerufen wird, und dass eine eingebaute Funktion namens next eingeführt wird, um die __next__-Methode aufzurufen, konsistent mit der Art und Weise, wie andere Protokolle explizit aufgerufen werden.

Namen mit doppelten Unterstrichen

In Python werden doppelte Unterstriche vor und nach einem Namen verwendet, um Namen zu unterscheiden, die zur Sprache selbst gehören. Attribute und Methoden, die vom Interpreter implizit verwendet oder erstellt werden, verwenden diese Namenskonvention; einige Beispiele sind

  • __file__ – ein vom Interpreter automatisch erstelltes Attribut
  • __dict__ – ein Attribut mit besonderer Bedeutung für den Interpreter
  • __init__ – eine vom Interpreter implizit aufgerufene Methode

Beachten Sie, dass diese Konvention für Methoden wie __init__ gilt, die vom Programmierer explizit definiert werden, sowie für Attribute wie __file__, die nur durch explizite Benennung zugänglich sind. Sie umfasst also Namen, die vom Interpreter verwendet *oder* erstellt werden.

(Nicht alles, was als „Protokoll“ bezeichnet wird, besteht aus Methoden mit Namen, die doppelte Unterstriche enthalten. Beispielsweise hat die Methode __contains__ doppelte Unterstriche, da der Sprachkonstrukt x in y implizit __contains__ aufruft. Aber obwohl die Methode read Teil des Dateiprotokolls ist, hat sie keine doppelten Unterstriche, da es keinen Sprachkonstrukt gibt, der x.read() implizit aufruft.)

Die Verwendung von doppelten Unterstrichen schafft einen separaten Namensraum für Namen, die Teil der Python-Sprachdefinition sind, sodass Programmierer Variablen, Attribute und Methoden, die mit Buchstaben beginnen, frei erstellen können, ohne Angst vor stillen Kollisionen mit Namen mit sprachdefinierter Bedeutung haben zu müssen. (Kollisionen mit reservierten Schlüsselwörtern sind immer noch ein Problem, aber zumindest führt dies sofort zu einem Syntaxfehler.)

Die Benennung der next-Methode für Iteratoren ist eine Ausnahme von dieser Konvention. Code, der nirgendwo einen expliziten Aufruf einer next-Methode enthält, kann dennoch stillschweigend durch die Anwesenheit einer solchen Methode beeinflusst werden. Daher schlägt dieser PEP vor, dass Iteratoren stattdessen eine __next__-Methode anstelle einer next-Methode haben sollten (ohne Änderung der Semantik).

Methoden und eingebaute Funktionen mit doppelten Unterstrichen

Die Python-Sprache definiert mehrere Protokolle, die durch die Definition von Methoden mit doppelten Unterstrichen implementiert oder angepasst werden. In jedem Fall wird das Protokoll von einer internen Methode bereitgestellt, die als C-Funktion im Interpreter implementiert ist. Für in Python definierte Objekte unterstützt diese C-Funktion die Anpassung durch implizites Aufrufen einer Python-Methode mit doppeltem Unterstrichnamen (sie führt oft zusätzlich zur reinen Aufrufung der Python-Methode noch kleine zusätzliche Arbeiten aus).

Manchmal wird das Protokoll durch eine syntaktische Konstruktion aufgerufen

  • x[y] –> interner tp_getitem –> x.__getitem__(y)
  • x + y –> interner nb_add –> x.__add__(y)
  • -x –> interner nb_negative –> x.__neg__()

Manchmal gibt es keine syntaktische Konstruktion, aber es ist dennoch nützlich, das Protokoll explizit aufrufen zu können. Für solche Fälle bietet Python eine eingebaute Funktion mit demselben Namen, aber ohne die doppelten Unterstriche.

  • len(x) –> interne sq_length –> x.__len__()
  • hash(x) –> interne tp_hash –> x.__hash__()
  • iter(x) –> interne tp_iter –> x.__iter__()

Nach diesem Muster ist der natürliche Weg, next zu behandeln, die Hinzufügung einer eingebauten Funktion next, die sich genau gleich verhält.

  • next(x) –> interne tp_iternext –> x.__next__()

Darüber hinaus wird vorgeschlagen, dass die eingebaute Funktion next einen Sentinel-Wert als optionales zweites Argument akzeptiert, im Stil der eingebauten Funktionen getattr und iter. Wenn next mit zwei Argumenten aufgerufen wird, fängt sie die StopIteration-Ausnahme ab und gibt stattdessen den Sentinel-Wert zurück, anstatt die Ausnahme weiterzuleiten. Dies schafft eine schöne Dualität zwischen iter und next.

iter(function, sentinel) <–> next(iterator, sentinel)

Vorherige Vorschläge

Dieser Vorschlag ist keine neue Idee. Die hier vorgeschlagene Idee wurde vom BDFL auf python-dev [1] unterstützt und wird sogar im ursprünglichen Iterator-PEP, PEP 234, erwähnt.

(In retrospect, it might have been better to go for __next__()
and have a new built-in, next(it), which calls it.__next__().
But alas, it's too late; this has been deployed in Python 2.2
since December 2001.)

Einwände

Es gab einige Einwände gegen die Hinzufügung weiterer eingebauter Funktionen. Insbesondere schreibt Martin von Loewis [2]

I dislike the introduction of more builtins unless they have a true
generality (i.e. are likely to be needed in many programs). For this
one, I think the normal usage of __next__ will be with a for loop, so
I don't think one would often need an explicit next() invocation.

It is also not true that most protocols are explicitly invoked through
builtin functions. Instead, most protocols are can be explicitly invoked
through methods in the operator module. So following tradition, it
should be operator.next.

...

As an alternative, I propose that object grows a .next() method,
which calls __next__ by default.

Migrationsplan

Zwei zusätzliche Transformationen werden dem 2to3-Übersetzungstool hinzugefügt [3]

  • Methodendefinitionen namens next werden in __next__ umbenannt.
  • Explizite Aufrufe der next-Methode werden durch Aufrufe der eingebauten next-Funktion ersetzt. Zum Beispiel wird aus x.next() next(x).

Collin Winter untersuchte die Möglichkeit, automatisch zu entscheiden, ob die zweite Transformation durchgeführt werden soll, abhängig von der Anwesenheit einer Modul-Ebene-Bindung an next [4] und fand heraus, dass es „hässlich und langsam“ wäre. Stattdessen gibt das Übersetzungstool Warnungen aus, wenn eine solche Bindung erkannt wird. Collin hat Warnungen für die folgenden Bedingungen vorgeschlagen [5]

  • Modul-Ebene-Zuweisungen zu next.
  • Modul-Ebene-Definitionen einer Funktion namens next.
  • Modul-Ebene-Importe des Namens next.
  • Zuweisungen an __builtin__.next.

Genehmigung

Dieser PEP wurde von Guido am 6. März 2007 akzeptiert [6].

Implementierung

Ein Patch mit den notwendigen Änderungen (außer dem 2to3-Tool) wurde von Georg Brandl geschrieben und als Revision 54910 eingepflegt.

Referenzen


Quelle: https://github.com/python/peps/blob/main/peps/pep-3114.rst

Zuletzt geändert: 2025-02-01 08:55:40 GMT