PEP 201 – Lockstep Iteration
- Author:
- Barry Warsaw <barry at python.org>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 13-Jul-2000
- Python-Version:
- 2.0
- Post-History:
- 27-Jul-2000
Introduction
This PEP describes the ‘lockstep iteration’ proposal. This PEP tracks the status and ownership of this feature, slated for introduction in Python 2.0. It contains a description of the feature and outlines changes necessary to support the feature. This PEP summarizes discussions held in mailing list forums, and provides URLs for further information, where appropriate. The CVS revision history of this file contains the definitive historical record.
Motivation
Standard for-loops in Python iterate over every element in a sequence until the sequence is exhausted [1]. However, for-loops iterate over only a single sequence, and it is often desirable to loop over more than one sequence in a lock-step fashion. In other words, in a way such that the i-th iteration through the loop returns an object containing the i-th element from each sequence.
The common idioms used to accomplish this are unintuitive. This PEP
proposes a standard way of performing such iterations by introducing a
new builtin function called zip.
While the primary motivation for zip() comes from lock-step iteration, by implementing zip() as a built-in function, it has additional utility in contexts other than for-loops.
Lockstep For-Loops
Lockstep for-loops are non-nested iterations over two or more sequences, such that at each pass through the loop, one element from each sequence is taken to compose the target. This behavior can already be accomplished in Python through the use of the map() built-in function:
>>> 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)]
The for-loop simply iterates over this list as normal.
While the map() idiom is a common one in Python, it has several disadvantages:
- It is non-obvious to programmers without a functional programming background.
- The use of the magic
Nonefirst argument is non-obvious. - It has arbitrary, often unintended, and inflexible semantics when
the lists are not of the same length: the shorter sequences are
padded with
None:>>> c = (4, 5, 6, 7) >>> map(None, a, c) [(1, 4), (2, 5), (3, 6), (None, 7)]
For these reasons, several proposals were floated in the Python 2.0 beta time frame for syntactic support of lockstep for-loops. Here are two suggestions:
for x in seq1, y in seq2:
# stuff
for x, y in seq1, seq2:
# stuff
Neither of these forms would work, since they both already mean something in Python and changing the meanings would break existing code. All other suggestions for new syntax suffered the same problem, or were in conflict with other another proposed feature called ‘list comprehensions’ (see PEP 202).
The Proposed Solution
The proposed solution is to introduce a new built-in sequence
generator function, available in the __builtin__ module. This
function is to be called zip and has the following signature:
zip(seqa, [seqb, [...]])
zip() takes one or more sequences and weaves their elements
together, just as map(None, ...) does with sequences of equal
length. The weaving stops when the shortest sequence is exhausted.