For small data sets you will not find much difference. But normally if you want to chain and iterate multiple iterables, then you can use itertools.chain
, like this
>>> list1, list2, list3 = [0, 1], ['a', 'b'], [2, 3]
>>> from itertools import chain
>>> for item in chain(list1, list2, list3):
... print(item)
0
1
a
b
2
3
This doesn't create any intermediate data structures and iterate each of the iterables one by one. The value returned by chain
is an iterator. So that also doesn't create a container with all the items in it and it is very memory efficient if the iterables are going to be very big.
And itertools.chain
is effectively the same as your second approach. Quoting the equivalent implementation from the official docs
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
If we look at the byte code generated for the first program you have shown, with this
from dis import dis
list1, list2, list3 = [0, 1], ['a', 'b'], [2, 3]
def func():
for item in list1 + list2 + list3:
print(item)
dis(func)
it would be something like
6 0 SETUP_LOOP 27 (to 30)
3 LOAD_GLOBAL 0 (list1)
6 LOAD_GLOBAL 1 (list2)
9 BINARY_ADD
10 LOAD_GLOBAL 2 (list3)
13 BINARY_ADD
14 GET_ITER
>> 15 FOR_ITER 11 (to 29)
18 STORE_FAST 0 (item)
7 21 LOAD_FAST 0 (item)
24 PRINT_ITEM
25 PRINT_NEWLINE
26 JUMP_ABSOLUTE 15
>> 29 POP_BLOCK
>> 30 LOAD_CONST 0 (None)
33 RETURN_VALUE
As you can see, the BINARY_ADD
code is used twice. It means that list1
and list2
are added first and a temporary list is created and that is again added with list3
. This will be highly inefficient if any of the lists is very big.