I have been really fascinated by all the interesting iterators in itertools
, but one confusion I have had is the difference between these two functions and why chain.from_iterable
exists.
from itertools import chain
def foo(n):
for i in range(n):
yield [i, i**2]
chain(*foo(5))
chain.from_iterable(foo(5))
What is the difference between the two functions?
The former can only handle unpackable iterables. The latter can handle iterables that cannot be fully unpacked, such as infinite generators.
Consider
>>> from itertools import chain
>>> def inf():
... i=0
... while True:
... i += 1
... yield (i, i)
...
>>> x=inf()
>>> y=chain.from_iterable(x)
>>> z=chain(*x)
<hangs forever>
Furthermore, just the act of unpacking is an eager, up-front-cost activity, so if your iterable has effects you want to evaluate lazily, from_iterable
is your best option.
chain(*foo(5))
unpacks the whole generator, packs it into a tuple and processes it then.
chain.from_iterable(foo(5))
queries the generator created from foo(5)
value for value.
Try foo(1000000)
and watch the memory usage go up and up.
*
unpacks the iterator, meaning it iterates the iterator in order to pass its values to the function. chain.from_iterable
iterates the iterator one by one lazily.
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