According to this answer lists perform better than generators in a number of cases, for example when used together with str.join
(since the algorithm needs to pass over the data twice).
In the following example using a list comprehension seems to yield better performance than using a corresponding generator expression though intuitively the list comprehension comes with an overhead of allocating and copying to additional memory which the generator sidesteps.
In [1]: l = list(range(2_000_000))
In [2]: %timeit l[:] = [i*3 for i in range(len(l))]
190 ms ± 4.65 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [3]: %timeit l[:] = (i*3 for i in range(len(l)))
261 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [4]: %timeit l[::2] = [i*3 for i in range(len(l)//2)]
97.1 ms ± 2.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [5]: %timeit l[::2] = (i*3 for i in range(len(l)//2))
129 ms ± 2.21 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [6]: %timeit l[:len(l)//2] = [i*3 for i in range(len(l)//2)]
92.6 ms ± 2.34 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [7]: %timeit l[:len(l)//2] = (i*3 for i in range(len(l)//2))
118 ms ± 2.17 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Why does a list comprehension yield better performance in these cases?
l[:]
is a slice, so to make the types match up, the generator has to be converted to a list behind the scenes – Tropol[:] = ...
is equivalent tol.__setitem__(slice(None), ...)
but why does the generator need to be converted to a list? – WhicheverIf the target is a slicing: The primary expression in the reference is evaluated. It should yield a mutable sequence object (such as a list). The assigned object should be a sequence object of the same type.
Thus, a generator must be coerced tolist
type – Tropofor x in [i for i in range(10_000)]: pass
andfor x in (i for i in range(10_000)): pass
And you will see that even if you have to do two passes with the list comprehension version, iteration is still over-all faster with the list comprehension. I don't start seeing the generator expression winning until we are working with about 1_000_000 items, and even then it's only marginally faster... – Hockett