When iterating over range() , objects are created for all integers from 0 to n ; it takes a (small) amount of time, even if small integers have been cached .
On the other hand, a loop through [None] * n creates n links to 1 object, and creating this list is a little faster.
However, the range() object uses much less memory and is more readable for loading, so people prefer to use this. Most codes should not squeeze every last frame out of performance.
If you need this speed, you can use a custom iterative that doesn't accept memory using itertools.repeat() with a second argument
from itertools import repeat for _ in repeat(None, n):
As for your time tests, there are problems with them.
First of all, you made a mistake in the synchronization cycle ['']*n ; you did not insert two quotation marks, you combined the two lines and created an empty list:
>>> '['']*n' '[]*n' >>> []*100 []
This will be invincible in iteration since you repeated 0 times.
You also did not use large numbers; ^ is a binary XOR operator, not a power operator:
>>> 10^1000 994
which means your test missed how long it took to create a large list of empty values.
Using the best numbers and None gives you:
>>> from timeit import timeit >>> 10 ** 6 1000000 >>> timeit("for _ in range(10 ** 6): pass", number=100) 3.0651066239806823 >>> timeit("for _ in [None] * (10 ** 6): pass", number=100) 1.9346517859958112 >>> timeit("for _ in repeat(None, 10 ** 6): pass", 'from itertools import repeat', number=100) 1.4315521717071533