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)