PEP 201 – Lockstep-Iteration
- Autor:
- Barry Warsaw <barry at python.org>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 13-Jul-2000
- Python-Version:
- 2.0
- Post-History:
- 27. Jul. 2000
Einleitung
Diese PEP beschreibt den Vorschlag für „Lockstep-Iteration“. Diese PEP dokumentiert den Status und die Verantwortlichkeit für diese Funktion, die für die Einführung in Python 2.0 geplant ist. Sie enthält eine Beschreibung der Funktion und skizziert die notwendigen Änderungen zur Unterstützung der Funktion. Diese PEP fasst Diskussionen in Mailinglisten zusammen und liefert, wo angebracht, URLs für weitere Informationen. Die CVS-Revisionshistorie dieser Datei enthält die definitive historische Aufzeichnung.
Motivation
Standardmäßige For-Schleifen in Python iterieren über jedes Element einer Sequenz, bis die Sequenz erschöpft ist [1]. For-Schleifen iterieren jedoch nur über eine einzelne Sequenz, und es ist oft wünschenswert, über mehr als eine Sequenz im Lockstep-Verfahren zu iterieren. Mit anderen Worten, auf eine Weise, so dass die i-te Iteration der Schleife ein Objekt zurückgibt, das das i-te Element aus jeder Sequenz enthält.
Die üblichen Idiome zur Erreichung dieses Ziels sind unintuitiv. Diese PEP schlägt einen Standardweg zur Durchführung solcher Iterationen vor, indem eine neue eingebaute Funktion namens zip eingeführt wird.
Obwohl die primäre Motivation für zip() aus der Lockstep-Iteration stammt, hat die Implementierung von zip() als eingebaute Funktion zusätzlichen Nutzen in anderen Kontexten als For-Schleifen.
Lockstep-For-Schleifen
Lockstep-For-Schleifen sind nicht-verschachtelte Iterationen über zwei oder mehr Sequenzen, so dass bei jedem Durchlauf der Schleife ein Element aus jeder Sequenz entnommen wird, um das Ziel zu bilden. Dieses Verhalten kann in Python bereits durch die Verwendung der eingebauten Funktion map() erreicht werden
>>> a = (1, 2, 3)
>>> b = (4, 5, 6)
>>> for i in map(None, a, b): print i
...
(1, 4)
(2, 5)
(3, 6)
>>> map(None, a, b)
[(1, 4), (2, 5), (3, 6)]
Die For-Schleife iteriert einfach wie gewohnt über diese Liste.
Obwohl das map()-Idiom in Python verbreitet ist, hat es mehrere Nachteile
- Es ist für Programmierer ohne funktionalen Programmierhintergrund nicht offensichtlich.
- Die Verwendung des magischen
Noneals erstes Argument ist nicht offensichtlich. - Es hat willkürliche, oft unbeabsichtigte und unflexible Semantiken, wenn die Listen nicht gleich lang sind: die kürzeren Sequenzen werden mit
Noneaufgefüllt>>> c = (4, 5, 6, 7) >>> map(None, a, c) [(1, 4), (2, 5), (3, 6), (None, 7)]
Aus diesen Gründen wurden im Zeitrahmen der Python 2.0 Beta-Version verschiedene Vorschläge für syntaktische Unterstützung von Lockstep-For-Schleifen eingebracht. Hier sind zwei Vorschläge
for x in seq1, y in seq2:
# stuff
for x, y in seq1, seq2:
# stuff
Keine dieser Formen würde funktionieren, da sie in Python bereits eine Bedeutung haben und die Änderung ihrer Bedeutung bestehenden Code brechen würde. Alle anderen Vorschläge für neue Syntax litten unter demselben Problem oder standen in Konflikt mit einer anderen vorgeschlagenen Funktion namens „List Comprehensions“ (siehe PEP 202).
Die vorgeschlagene Lösung
Die vorgeschlagene Lösung besteht darin, eine neue eingebaute Sequenzgeneratorfunktion einzuführen, die im Modul __builtin__ verfügbar ist. Diese Funktion soll zip heißen und hat die folgende Signatur
zip(seqa, [seqb, [...]])
zip() nimmt eine oder mehrere Sequenzen und verwebt deren Elemente, ähnlich wie map(None, ...) bei gleich langen Sequenzen. Das Verweben stoppt, wenn die kürzeste Sequenz erschöpft ist.
Rückgabewert
zip() gibt eine echte Python-Liste zurück, genau wie map().
Beispiele
Hier sind einige Beispiele, basierend auf der nachstehenden Referenzimplementierung
>>> a = (1, 2, 3, 4)
>>> b = (5, 6, 7, 8)
>>> c = (9, 10, 11)
>>> d = (12, 13)
>>> zip(a, b)
[(1, 5), (2, 6), (3, 7), (4, 8)]
>>> zip(a, d)
[(1, 12), (2, 13)]
>>> zip(a, b, c, d)
[(1, 5, 9, 12), (2, 6, 10, 13)]
Beachten Sie, dass zip() umkehrbar ist, wenn die Sequenzen gleich lang sind
>>> a = (1, 2, 3)
>>> b = (4, 5, 6)
>>> x = zip(a, b)
>>> y = zip(*x) # alternatively, apply(zip, x)
>>> z = zip(*y) # alternatively, apply(zip, y)
>>> x
[(1, 4), (2, 5), (3, 6)]
>>> y
[(1, 2, 3), (4, 5, 6)]
>>> z
[(1, 4), (2, 5), (3, 6)]
>>> x == z
1
Es ist nicht möglich, zip auf diese Weise umzukehren, wenn die Sequenzen nicht alle gleich lang sind.
Referenzimplementierung
Hier ist eine Referenzimplementierung der eingebauten Funktion zip() in Python. Diese wird nach endgültiger Genehmigung durch eine C-Implementierung ersetzt werden
def zip(*args):
if not args:
raise TypeError('zip() expects one or more sequence arguments')
ret = []
i = 0
try:
while 1:
item = []
for s in args:
item.append(s[i])
ret.append(tuple(item))
i = i + 1
except IndexError:
return ret
BDFL-Bekanntmachungen
Hinweis: BDFL bezieht sich auf Guido van Rossum, Pythons Wohlwollenden Diktator auf Lebenszeit.
- Der Name der Funktion. Eine frühere Version dieser PEP enthielt eine offene Frage mit über 20 vorgeschlagenen Alternativnamen für
zip(). Da keine überwältigend bessere Wahl existierte, bevorzugt der BDFLzip()aufgrund seiner Haskell-Herkunft [2] stark. Sehen Sie sich Version 1.7 dieser PEP für die Liste der Alternativen an. zip()soll eine eingebaute Funktion sein.- Optionale Auffüllung. Eine frühere Version dieser PEP schlug ein optionales Schlüsselwortargument
padvor, das verwendet werden sollte, wenn die Argumentsequenzen nicht gleich lang waren. Dieses Verhalten ähnelt dem vonmap(None, ...), außer dass der Benutzer das Auffüllobjekt angeben konnte. Dies wurde vom BDFL zugunsten des immerwährenden Abschneidens auf die kürzeste Sequenz abgelehnt, wegen des KISS-Prinzips. Wenn ein echter Bedarf besteht, ist es einfacher, ihn später hinzuzufügen. Wenn er nicht benötigt wird, wäre es dennoch unmöglich, ihn in Zukunft zu löschen. - Lazy Evaluation. Eine frühere Version dieser PEP schlug vor, dass
zip()ein eingebautes Objekt zurückgibt, das Lazy Evaluation über das__getitem__()-Protokoll durchführt. Dies wurde vom BDFL stark abgelehnt zugunsten der Rückgabe einer echten Python-Liste. Wenn in Zukunft Lazy Evaluation gewünscht wird, schlägt der BDFL vor, eine Funktionxzip()hinzuzufügen. zip()ohne Argumente. Der BDFL bevorzugt es stark, dass dies eine TypeError-Ausnahme auslöst.zip()mit einem Argument. Der BDFL bevorzugt es stark, dass dies eine Liste von 1-Tupeln zurückgibt.- Kontrolle der inneren und äußeren Container. Eine frühere Version dieser PEP enthält eine recht ausführliche Diskussion über eine Funktion, die einige Leute wollten, nämlich die Möglichkeit, die Typen der inneren und äußeren Container zu steuern (es sind in dieser Version der PEP Tupel und Listen). Angesichts der vereinfachten API und Implementierung wird diese Ausarbeitung abgelehnt. Für eine detailliertere Analyse siehe Version 1.7 dieser PEP.
Nachfolgende Änderung an zip()
In Python 2.4 wurde zip() ohne Argumente so geändert, dass es eine leere Liste zurückgibt, anstatt eine TypeError-Ausnahme auszulösen. Die Begründung für das ursprüngliche Verhalten war, dass die Abwesenheit von Argumenten als Programmierfehler angesehen wurde. Dieses Denken hatte jedoch nicht die Verwendung von zip() mit dem *-Operator zum Entpacken von variablen Argumentlisten vorhergesehen. Zum Beispiel könnte die Umkehrung von zip definiert werden als: unzip = lambda s: zip(*s). Diese Transformation definiert auch eine Matrix-Transponierung oder einen äquivalenten Zeilen-/Spaltentausch für Tabellen, die als Listen von Tupeln definiert sind. Die letztere Transformation wird häufig beim Lesen von Datendateien verwendet, bei denen Datensätze Zeilen und Felder Spalten sind. Zum Beispiel der Code
date, rain, high, low = zip(*csv.reader(file("weather.csv")))
ordnet Spaltendaten neu an, so dass jedes Feld in einzelne Tupel für einfaches Schleifen und Zusammenfassen gesammelt wird
print "Total rainfall", sum(rain)
Die Verwendung von zip(*args) ist einfacher zu codieren, wenn zip(*[]) als zulässiger Fall und nicht als Ausnahme behandelt wird. Dies ist besonders hilfreich, wenn Daten entweder aus einem Nullfall ohne Datensätze aufgebaut oder auf ihn rekursiv reduziert werden.
Angesichts dieser Möglichkeit stimmte der BDFL (mit einigen Bedenken) zu, das Verhalten für Py2.4 zu ändern.
Andere Änderungen
- Die oben diskutierte Funktion
xzip()wurde in Py2.3 im Modulitertoolsalsitertools.izip()implementiert. Diese Funktion bietet Lazy Evaluation, verbraucht einzelne Elemente und erzeugt bei jedem Durchgang ein einzelnes Tupel. Der „Just-in-Time“-Stil spart Speicher und ist schneller als sein listenbasierter Gegenpartzip(). - Das Modul
itertoolsfügte auchitertools.repeat()unditertools.chain()hinzu. Diese Werkzeuge können zusammen verwendet werden, um Sequenzen mitNoneaufzufüllen (um das Verhalten vonmap(None, seqn)zu erreichen)zip(firstseq, chain(secondseq, repeat(None)))
Referenzen
Greg Wilsons Fragebogen zu vorgeschlagener Syntax für einige CS-Studenten https://pythonlang.de/pipermail/python-dev/2000-July/013139.html
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0201.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT