PEP 322 – Reverse Iteration
- Autor:
- Raymond Hettinger <python at rcn.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 24. Sep. 2003
- Python-Version:
- 2.4
- Post-History:
- 24. Sep. 2003
Zusammenfassung
Dieser Vorschlag sieht die Einführung einer integrierten Funktion zur Unterstützung der Rückwärtsiteration über Sequenzen vor.
Motivation
Bei indizierbaren Objekten sind aktuelle Ansätze für die Rückwärtsiteration fehleranfällig, unnatürlich und nicht besonders lesbar.
for i in xrange(n-1, -1, -1):
print seqn[i]
Ein anderer aktueller Ansatz beinhaltet das Umdrehen einer Liste vor der Iteration. Diese Technik verschwendet Rechenzyklen, Speicher und Codezeilen.
rseqn = list(seqn)
rseqn.reverse()
for value in rseqn:
print value
Erweitertes Slicing ist ein dritter Ansatz, der den Codeaufwand minimiert, aber nichts für die Speichereffizienz, die Ästhetik oder die Klarheit tut.
Rückwärtsiteration ist weitaus seltener als Vorwärtsiteration, kommt aber in der Praxis regelmäßig vor. Siehe unten Real World Use Cases.
Vorschlag
Fügen Sie eine integrierte Funktion namens reversed() hinzu, die einen Rückwärtsiterator über Sequenzobjekte erstellt, die __getitem__() und __len__() unterstützen.
Die obigen Beispiele vereinfachen sich dann zu
for i in reversed(xrange(n)):
print seqn[i]
for elem in reversed(seqn):
print elem
Die Kernidee ist, dass der klarste, am wenigsten fehleranfällige Weg, die Rückwärtsiteration zu spezifizieren, darin besteht, sie in Vorwärtsrichtung zu spezifizieren und dann reversed zu sagen.
Die Implementierung könnte so einfach sein wie
def reversed(x):
if hasattr(x, 'keys'):
raise ValueError("mappings do not support reverse iteration")
i = len(x)
while i > 0:
i -= 1
yield x[i]
Es sind keine Sprachsyntaxänderungen erforderlich. Der Vorschlag ist vollständig abwärtskompatibel.
Eine C-Implementierung und Unit-Tests finden Sie unter: https://bugs.python.org/issue834422
BDFL-Verkündigung
Dieses PEP wurde bedingt für Py2.4 angenommen. Die Bedingung bedeutet, dass die Funktion, wenn sie als nutzlos erachtet wird, vor Py2.4b1 entfernt werden kann.
Alternative Method Names
- reviter – Jeremy Finchers Vorschlag passt zur Verwendung von iter()
- ireverse – verwendet die Namenskonvention von itertools
- inreverse – niemand scheint diese außer mir zu mögen
Der Name reverse ist kein Kandidat, da er den Namen von list.reverse() dupliziert, der die zugrundeliegende Liste mutiert.
Diskussion
Das Argument gegen die Annahme des PEP ist der Wunsch, die Anzahl der integrierten Funktionen gering zu halten. Dies muss gegen die Einfachheit und Bequemlichkeit abgewogen werden, sie als integrierte Funktion zu haben, anstatt sie in einem anderen Namensraum zu verstecken.
Real World Use Cases
Hier sind einige Beispiele für Rückwärtsiteration aus der Standardbibliothek und Kommentare, warum Rückwärtsiteration notwendig war.
- atexit.exit_handlers() verwendet
while _exithandlers: func, targs, kargs = _exithandlers.pop() . . .
In dieser Anwendung ist das Popping erforderlich, daher würde die neue Funktion nicht helfen.
- heapq.heapify() verwendet
for i in xrange(n//2 - 1, -1, -1), da höhere Ordnungen leichter aus Paaren niedrigerer Ordnungen gebildet werden. Eine Vorwärtsversion dieses Algorithmus ist möglich; dies würde jedoch den Rest des Heap-Codes komplizieren, der in umgekehrter Richtung über die zugrunde liegende Liste iteriert. Der Ersatzcodefor i in reversed(xrange(n//2))macht den abgedeckten Bereich und die Anzahl der Iterationen klar. - mhlib.test() verwendet
testfolders.reverse(); for t in testfolders: do('mh.deletefolder(%s)' % `t`)Die Notwendigkeit der Rückwärtsiteration ergibt sich daraus, dass das Ende der zugrunde liegenden Liste während der Iteration verändert wird.
- platform._dist_try_harder() verwendet
for n in range(len(verfiles)-1,-1,-1), da die Schleife ausgewählte Elemente aus verfiles löscht, aber den Rest der Liste für weitere Iterationen intakt lassen muss. - random.shuffle() verwendet
for i in xrange(len(x)-1, 0, -1), da der Algorithmus am einfachsten als zufällige Auswahl von Elementen aus einem sich ständig verringernden Pool verstanden wird. Tatsächlich kann der Algorithmus in Vorwärtsrichtung ausgeführt werden, ist aber weniger intuitiv und wird in der Literatur selten so dargestellt. Der Ersatzcodefor i in reversed(xrange(1, len(x)))ist visuell viel einfacher zu überprüfen. - rfc822.Message.__delitem__() verwendet
list.reverse() for i in list: del self.headers[i]
Die Notwendigkeit der Rückwärtsiteration ergibt sich daraus, dass das Ende der zugrunde liegenden Liste während der Iteration verändert wird.
Abgelehnte Alternativen
Es wurden mehrere Varianten eingereicht, die versuchten, reversed() auf alle Iterables anzuwenden, indem das Iterable bis zum Ende durchlaufen, die Ergebnisse gespeichert und dann ein Rückwärtsiterator über die Ergebnisse zurückgegeben wurde. Während dies einige Vorstellungen von vollständiger Allgemeinheit erfüllt, widerspricht das Durchlaufen der Eingabe bis zum Ende dem Zweck der Verwendung von Iteratoren. Außerdem tritt eine kleine Katastrophe ein, wenn der zugrunde liegende Iterator unendlich ist.
Die Platzierung der Funktion in einem anderen Modul oder die Zuordnung zu einem Typobjekt wird nicht in Betracht gezogen. Wie ihre Cousins, zip() und enumerate(), muss die Funktion im täglichen Programmieren direkt zugänglich sein. Jede löst ein grundlegendes Schleifenproblem: Gleichschritt-Iteration, Schleifenzählung und Rückwärtsiteration. Die Anforderung einer Art Punktnotation würde ihre Einfachheit, ihren täglichen Nutzen und ihre Zugänglichkeit beeinträchtigen. Es handelt sich um Kernschleifenkonstrukte, die unabhängig von einem einzelnen Anwendungsbereich sind.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0322.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT