I found a case when map() usage isn't equivalent to a list comprehension. It happens when next used as the first argument.
For example:
l1 = [1, 2]
l2 = ['hello', 'world']
iterators = [iter(l1), iter(l2)]
# list comprehension
values1 = [next(it) for it in iterators]
# values1 = [1, "hello"]
values2 = [next(it) for it in iterators]
# values2 = [2, "world"]
values3 = [next(it) for it in iterators]
# raise StopIteration
l1 = [1, 2]
l2 = ['hello', 'world']
iterators = [iter(l1), iter(l2)]
# map
values1 = list(map(next, iterators))
# values1 = [1, "hello"]
values2 = list(map(next, iterators))
# values2 = [2, "world"]
values3 = list(map(next, iterators))
# values3 = []
# doesn't raise StopIteration
Any other exceptions occur as they should. Example:
def divide_by_zero(value: int):
return value // 0
l = [1, 2, 3]
values = list(map(divide_by_zero, l))
# raises ZeroDivisionError as expected
values = [divide_by_zero(value) for value in l]
# raises ZeroDivisionError as expected, too
It seems very strange. It works the same with Python 3.9 and Python 3.11.
It seems like map() works like this:
def map(func, iterator):
try:
while True:
item = next(iterator)
yield func(item)
except StopIteration:
pass
but I expected it to work like this:
def map(func, iterator):
while True:
try:
item = next(iterator)
except StopIteration:
break
yield func(item)
Is it a bug?
Try calling next on map:
>>> >>> m = map(next, iterators)
>>> next(m)
1
>>> next(m)
'hello'
>>> next(m)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
It's list that sees StopIteration and uses it to stop building the list from what map yields.
The list comprehension, on the other hand, is building the list by iterating over iterators, not a particular iterator in that list. That is, next(it) is used to produce a value for the list, not to determine if we've reached the end of iterators.
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