Consider the following example:
s = 'abc'
[(c1, c2) for j, c2 in enumerate(s) for i, c1 in enumerate(s)]
Output:
[('a', 'a'),
('b', 'a'),
('c', 'a'),
('a', 'b'),
('b', 'b'),
('c', 'b'),
('a', 'c'),
('b', 'c'),
('c', 'c')]
I would expected the same output if enumerate is called outside the list comprehension and the iterators are assigned to variables:
it1, it2 = enumerate(s), enumerate(s)
[(c1, c2) for j, c2 in it1 for i, c1 in it2]
But I get:
[('a', 'a'), ('b', 'a'), ('c', 'a')]
What is going on? I use Python 3.6.9.
Iterate over multiple lists at a time We can iterate over lists simultaneously in ways: zip() : In Python 3, zip returns an iterator. zip() function stops when anyone of the list of all the lists gets exhausted. In simple words, it runs till the smallest of all the lists.
In this example, you assign the return value of enumerate() to enum . enumerate() is an iterator, so attempting to access its values by index raises a TypeError . Fortunately, Python's enumerate() lets you avoid all these problems.
These include the string, list, tuple, dict, set, and frozenset types. But these are by no means the only types that you can iterate over. Many objects that are built into Python or defined in modules are designed to be iterable.
Definition: An iterable is any Python object capable of returning its members one at a time, permitting it to be iterated over in a for-loop. Familiar examples of iterables include lists, tuples, and strings - any such sequence can be iterated over in a for-loop.
What is happening is that the inner iterator gets exhausted after the first iteration of the outer iterator:
s = 'abc'
it1 = enumerate(s)
it2 = enumerate(s)
for i, x in it1:
for j, y in it2: # <-- gets consumed when i = 0 and stays empty
...
By contrast:
s = 'abc'
for i, x in enumerate(s):
for j, y in enumerate(s): # <-- gets recreated at each iteration
....
If you need persistence, enclose it in a list
or tuple
:
itr = list(enumerate(s))
print([(c1, c2) for j, c2 in itr for i, c1 in itr])
# [('a', 'a'), ('b', 'a'), ('c', 'a'), ('a', 'b'), ('b', 'b'), ('c', 'b'), ('a', 'c'), ('b', 'c'), ('c', 'c')]
although note the different memory footprint of using enumerate()
multiple times versus having it enclosed in a list
or tuple
.
The difference is that in:
s = 'abc'
[(c1, c2) for j, c2 in enumerate(s) for i, c1 in enumerate(s)]
A new c1
enumerator is created for each value yielded on the first for
. While on your second example, the same enumerator is used (it2) - and it gets exausted once it reaches "c" - when the first for
advances to the next iteration (c2 = "b") and tries to iterate "it2", it is already exhausted - and the whole expression ends.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With