Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> c = [1, 2]
>>> c += map(lambda n: n *2, range(1, 3))
>>> c
[1, 2, 2, 4]
>>> c = [1, 2]
>>> c += map(lambda n: n *2, c)
Killed!
However the same works with Python 2 where map results in a list unlike a generator in Python 3. Then why does c += map(lambda n: n *2, range(1, 3))
work?
c += foo
does the equivalent of c = c.__iadd__(foo)
. And if c
is a list, the list.__iadd__
method accepts any iterable, including a generator expression:
>>> x = []
>>> x += (i for i in range(3))
>>> x
[0, 1, 2]
this even though list.__add__
does not support generator expressions:
>>> x + (i for i in range(3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "generator") to list
The code crashes in Python 3 because the map
generator iterates over the same list that is being extended, but the current position is always two elements behind the tail. The list.__iadd__
will always add new elements to the end one by one as they are extracted from the iterator; as this happens, the index used by the list iterator held by the map
generator will be incremented... but a new element was added to the end of the list so there are always 2 elements after the current iteration position, and eventually Python runs out of memory or the computer grinds to halt due to swapping.
I.e. the behaviour is not unlike that of
for elem in c:
c.append(elem * 2)
(kudos to Wim)
The Python 2 version works exactly because map
will create a list before calling c.__iadd__
; a new list of 2 elements will be passed to c.__iadd__
. Likewise the c += map(lambda n: n * 2, range(1, 3))
would work because in Python 2 range(1, 3)
would create a new list, and in Python 3 it would create a range sequence object, and both of them are distinct
If you replace the map
with a generator expression, you can trigger pathological behaviour in Python 2 too:
Python 2.7.15rc1 (default, Nov 12 2018, 14:31:15)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = [1, 2]
>>> x += (n * 2 for n in x)
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