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

Python Enhancement Proposals

PEP 284 – Integer for-loops

Autor:
David Eppstein <eppstein at ics.uci.edu>, Gregory Ewing <greg.ewing at canterbury.ac.nz>
Status:
Abgelehnt
Typ:
Standards Track
Erstellt:
01-März 2002
Python-Version:
2.3
Post-History:


Inhaltsverzeichnis

Zusammenfassung

Dieser PEP schlägt vor, die Iteration über Integer-Intervalle zu vereinfachen, indem der Bereich der Ausdrücke nach dem Schlüsselwort „for“ erweitert wird, um Drei-Wege-Vergleiche wie

for lower <= var < upper:

anstelle der aktuellen

for item in list:

Syntax zu erlauben. Die resultierende Schleife oder Listeniteration durchläuft alle Werte von var, die den Vergleich wahr machen, beginnend mit dem linken Endpunkt des gegebenen Intervalls.

Bekanntmachung

Dieser PEP wird abgelehnt. Es gab eine Reihe von behebaren Problemen mit dem Vorschlag (siehe die Korrekturen in Raymond Hettingers python-dev-Post vom 18. Juni 2005 [1]). Selbst mit den Korrekturen erhielt der Vorschlag keine Unterstützung. Insbesondere Guido stimmte der Prämisse nicht zu, dass das range()-Format behoben werden musste: „Der Sinn (vor 15 Jahren) von range() war es, keine Syntax für eine Schleife über Zahlen zu benötigen. Ich denke, das hat gut funktioniert und es gibt nichts, was behoben werden muss (außer dass range() ein Iterator werden muss, was es in Python 3.0 tun wird).“

Begründung

Eine der häufigsten Verwendungen von for-Schleifen in Python ist die Iteration über ein Integer-Intervall. Python stellt die Funktionen range() und xrange() zur Erzeugung von Listen und Iteratoren für solche Intervalle bereit, die für den häufigsten Fall am besten geeignet sind: halboffene Intervalle, die von Null ansteigen. Die range()-Syntax ist jedoch für offene oder geschlossene Intervalle umständlicher und es mangelt ihr an Symmetrie beim Umkehren der Iterationsreihenfolge. Darüber hinaus macht der Aufruf einer ungewohnten Funktion es für Neueinsteiger in Python schwierig, Code zu verstehen, der range() oder xrange() verwendet.

Der wahrgenommene Mangel an einer natürlichen, intuitiven Syntax für Integer-Iteration hat zu hitzigen Debatten auf der python-list geführt und mindestens vier PEPs vor dieser hervorgebracht. PEP 204 (abgelehnt) schlug vor, die Python-Slice-Syntax für Integer-Bereiche wiederzuverwenden, was zu einer kürzeren Syntax führte, aber das Lesbarkeitsproblem von range() mit mehreren Argumenten nicht löste. PEP 212 (zurückgestellt) schlug mehrere Syntaxen für die direkte Konvertierung einer Liste in eine Sequenz von Integer-Indizes vor, anstelle des aktuellen Idioms

range(len(list))

für eine solche Konvertierung, und PEP 281 schlägt vor, dasselbe Idiom zu vereinfachen, indem es als

range(list).

geschrieben werden kann. PEP 276 schlägt die automatische Konvertierung von Integern in Iteratoren vor, was den häufigsten halboffenen Fall vereinfacht, aber nicht die Komplexität anderer Arten von Intervallen adressiert. Zusätzliche Alternativen wurden auf der python-list diskutiert.

Die hier beschriebene Lösung besteht darin, einen Drei-Wege-Vergleich nach dem Schlüsselwort „for“ sowohl im Kontext einer for-Schleife als auch einer Listenkomprehension zu erlauben.

for lower <= var < upper:

Dies würde eine Iteration über ein Intervall von aufeinanderfolgenden Integern bewirken, beginnend mit der linken Grenze im Vergleich und endend mit der rechten Grenze. Die genauen Vergleichsoperationen würden bestimmen, ob das Intervall an einem oder beiden Enden offen oder geschlossen ist und ob die Integer in aufsteigender oder absteigender Reihenfolge betrachtet werden.

Diese Syntax entspricht eng der mathematischen Standardnotation und ist daher für Python-Neulinge wahrscheinlich vertrauter als die aktuelle range()-Syntax. Offene und geschlossene Intervallgrenzen sind gleichermaßen einfach auszudrücken, und die Umkehrung eines Integer-Intervalls kann einfach durch Vertauschen der beiden Grenzen und Umkehren der Vergleiche gebildet werden. Darüber hinaus würden die Semantik einer solchen Schleife eng einer Interpretation der bestehenden Python for-Schleifen ähneln.

for item in list

iteriert über genau die Werte von item, die den Ausdruck

item in list

wahr machen. Ebenso würde das neue Format

for lower <= var < upper:

über genau die Integer-Werte von var iterieren, die den Ausdruck

lower <= var < upper

wahr machen.

Spezifikation

Wir schlagen vor, die Syntax einer for-Anweisung zu erweitern, derzeit

for_stmt: "for" target_list "in" expression_list ":" suite
      ["else" ":" suite]

wie unten beschrieben

for_stmt: "for" for_test ":" suite ["else" ":" suite]
for_test: target_list "in" expression_list |
        or_expr less_comp or_expr less_comp or_expr |
        or_expr greater_comp or_expr greater_comp or_expr
less_comp: "<" | "<="
greater_comp: ">" | ">="

Ebenso schlagen wir vor, die Syntax von Listenkomprehensionen zu erweitern, derzeit

list_for: "for" expression_list "in" testlist [list_iter]

und ersetzt durch

list_for: "for" for_test [list_iter]

In allen Fällen unterliegt der durch for_test gebildete Ausdruck den gleichen Präzedenzregeln wie Vergleiche in Ausdrücken. Die beiden comp_operators in einem for_test müssen beide vom gleichen Typ sein, im Gegensatz zu verketteten Vergleichen in Ausdrücken, die keine solche Einschränkung haben.

Wir bezeichnen die beiden or_expr auf der linken und rechten Seite der for-Schleifen-Syntax als Grenzen der Schleife und das mittlere or_expr als Variable der Schleife. Wenn eine for-Schleife mit der neuen Syntax ausgeführt wird, werden die Ausdrücke für beide Grenzen ausgewertet und ein Iterator-Objekt erstellt, das durch alle Integer zwischen den beiden Grenzen gemäß den verwendeten Vergleichsoperationen iteriert. Der Iterator beginnt mit einer Ganzzahl, die gleich oder nahe der linken Grenze ist, und durchläuft dann die restlichen Ganzzahlen mit einer Schrittgröße von +1 oder -1, wenn die Vergleichsoperation in der durch less_comp bzw. greater_comp beschriebenen Menge enthalten ist. Die Ausführung wird dann fortgesetzt, als ob der Ausdruck gewesen wäre

for variable in iterator

wobei „variable“ sich auf die Variable der Schleife bezieht und „iterator“ sich auf den für das gegebene Integer-Intervall erstellten Iterator bezieht.

Die Werte, die die Schleifenvariable in einer Integer-for-Schleife annimmt, können je nach Größe der Grenzen entweder normale Integer oder lange Integer sein. Beide Grenzen einer Integer-for-Schleife müssen zu einem reellen numerischen Typ (Integer, Long oder Float) ausgewertet werden. Jeder andere Wert führt dazu, dass die for-Schleifenanweisung eine TypeError-Ausnahme auslöst.

Probleme

Die folgenden Probleme wurden in der Diskussion dieses und verwandter Vorschläge in der Python-Liste aufgeworfen.

  • Sollte die rechte Grenze einmal oder jedes Mal in der Schleife ausgewertet werden? Offensichtlich ist es nur sinnvoll, die linke Grenze einmal auszuwerten. Aus Gründen der Konsistenz und Effizienz haben wir die gleiche Konvention für die rechte Grenze gewählt.
  • Obwohl die neue Syntax Integer-for-Schleifen erheblich vereinfacht, sind Listenkomprehensionen mit der neuen Syntax nicht so einfach. Wir sind der Meinung, dass dies angemessen ist, da for-Schleifen häufiger vorkommen als Komprehensionen.
  • Der Vorschlag erlaubt keinen Zugriff auf Integer-Iterator-Objekte, wie sie von xrange erstellt würden. Das stimmt, aber wir sehen dies als Mangel in der allgemeinen Syntax für Listenkomprehensionen, der über den Rahmen dieses Vorschlags hinausgeht. Außerdem wird xrange() weiterhin verfügbar sein.
  • Der Vorschlag erlaubt keine anderen Inkremente als 1 und -1. Allgemeinere arithmetische Progressionen müssten mit range() oder xrange() oder durch eine Syntax für Listenkomprehensionen wie
    [2*x for 0 <= x <= 100]
    
  • erstellt werden. Die Position der Schleifenvariable in der Mitte eines Drei-Wege-Vergleichs ist nicht so offensichtlich wie die Variable in der aktuellen
    for item in list
    

    Syntax, was zu einem möglichen Verlust an Lesbarkeit führt. Wir sind der Meinung, dass dieser Verlust durch die Zunahme der Lesbarkeit durch eine natürliche Integer-Iterationssyntax aufgewogen wird.

  • Bis zu einem gewissen Grad befasst sich dieser PEP mit denselben Problemen wie PEP 276. Wir sind der Meinung, dass sich die beiden PEPs nicht widersprechen, da sich PEP 276 hauptsächlich mit halboffenen Bereichen ab 0 (dem einfachen Fall von range()) befasst, während sich dieser PEP hauptsächlich mit der Vereinfachung aller anderen Fälle befasst. Wenn dieser PEP jedoch genehmigt wird, könnte seine neue, einfachere Syntax für Integer-Schleifen die Motivation für PEP 276 bis zu einem gewissen Grad verringern.
  • Es ist nicht klar, ob es sinnvoll ist, Gleitkommazahlen als Grenzen für eine Integer-Schleife zuzulassen: Wenn eine Gleitkommazahl einen ungenauen Wert darstellt, wie kann sie dann zur Bestimmung einer exakten Sequenz von Integern verwendet werden? Andererseits würde das Verbot von Gleitkommazahl-Grenzen die Verwendung von floor() und ceiling() in Integer-for-Schleifen erschweren, da es auch mit range() schwierig ist. Wir haben uns für Flexibilität entschieden, aber dies kann zu einigen Implementierungsschwierigkeiten bei der Bestimmung der kleinsten und größten Integer-Werte führen, die einen gegebenen Vergleich wahr machen.
  • Sollten andere Typen als int, long und float als Grenzen zugelassen werden? Eine andere Wahl wäre, alle Grenzen mit int() in Integer zu konvertieren und alles zuzulassen, was so konvertiert werden kann, anstelle von nur Gleitkommazahlen. Dies würde jedoch die Semantik ändern: 0.3 <= x ist nicht dasselbe wie int(0.3) <= x, und es wäre verwirrend, wenn eine Schleife mit 0.3 als untere Grenze bei Null beginnt. Außerdem kann int(f) im Allgemeinen sehr weit von f entfernt sein.

Implementierung

Eine Implementierung ist derzeit nicht verfügbar. Die Implementierung wird voraussichtlich keine großen Schwierigkeiten bereiten: Die neue Syntax könnte bei Bedarf erkannt werden, indem ein allgemeiner Ausdruck nach jedem Schlüsselwort „for“ geparst und getestet wird, ob die höchste Operation des Ausdrucks „in“ oder ein Drei-Wege-Vergleich ist. Der Python-Compiler würde jede Instanz der neuen Syntax in eine Schleife über die Elemente eines speziellen Iterator-Objekts umwandeln.

Referenzen


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

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