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

Python Enhancement Proposals

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

Inhaltsverzeichnis

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 Ersatzcode for 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 Ersatzcode for 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.


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

Zuletzt geändert: 2025-02-01 08:59:27 GMT