Appending to list can be possibly done in two ways:
1)
mat = []
for i in range(10):
mat.append(i)
or
2)
mat = []
for i in range(10):
mat += [i]
From the official Python documentation:
The method append() shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent to result = result + [a], but more efficient.
The documentation suggests that approach 1 is more efficient. Why is it so?
Even though using .append
requires a method call it's actually slightly more efficient than using the augmented assignment operator, +=
.
But there's an even better reason to use .append
: you can do it when the list you're appending to isn't in the local scope, since it's just calling the method of an object in an outer scope, whereas you cannot perform assignments on objects that aren't in the local scope unless you declare them as global (or nonlocal), a practice which is generally best to avoid.
Here's an example:
mat = []
def test_append():
for i in range(10):
#mat += [i]
mat.append(i)
def test_iadd():
for i in range(10):
mat += [i]
test_append()
print(mat)
test_iadd()
print(mat)
output
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Traceback (most recent call last):
File "./qtest.py", line 29, in <module>
test_iadd()
File "./qtest.py", line 25, in test_iadd
mat += [i]
UnboundLocalError: local variable 'mat' referenced before assignment
Of course, we can pass mat
as an argument to the function:
mat = []
def test_append():
for i in range(10):
#mat += [i]
mat.append(i)
def test_iadd2(mat):
for i in range(10):
mat += [i]
test_append()
print(mat)
test_iadd2(mat)
print(mat)
output
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
One reason that test_iadd2
is slower is that it has to construct a new [i]
list on every loop.
FWIW, here's the bytecode:
test_append
21 0 SETUP_LOOP 33 (to 36)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (10)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 19 (to 35)
16 STORE_FAST 0 (i)
23 19 LOAD_GLOBAL 1 (mat)
22 LOAD_ATTR 2 (append)
25 LOAD_FAST 0 (i)
28 CALL_FUNCTION 1
31 POP_TOP
32 JUMP_ABSOLUTE 13
>> 35 POP_BLOCK
>> 36 LOAD_CONST 0 (None)
39 RETURN_VALUE
test_iadd2
26 0 SETUP_LOOP 33 (to 36)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (10)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 19 (to 35)
16 STORE_FAST 1 (i)
27 19 LOAD_FAST 0 (mat)
22 LOAD_FAST 1 (i)
25 BUILD_LIST 1
28 INPLACE_ADD
29 STORE_FAST 0 (mat)
32 JUMP_ABSOLUTE 13
>> 35 POP_BLOCK
>> 36 LOAD_CONST 0 (None)
39 RETURN_VALUE
The above bytecode dump was produced using the dis module:
from dis import dis
mat = []
def test_append():
for i in range(10):
#mat += [i]
mat.append(i)
def test_iadd2(mat):
for i in range(10):
mat += [i]
print('test_append')
dis(test_append)
print('\ntest_iadd2')
dis(test_iadd2)
import timeit
timeit.timeit('for i in range(10): mat.append(i)', 'mat = []')
1.798893928527832
timeit.timeit('for i in range(10): mat += [i]', 'mat = []')
2.547478199005127
method append
is faster, because it does't use any type conversion.
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