Why is list.append (x) more efficient than list + = l [x]?

Adding to the list can be done in two ways:

1)

mat = [] for i in range(10): mat.append(i) 

or

2)

 mat = [] for i in range(10): mat += [i] 

In the official Python documentation:

The append () method shown in the example is defined for list objects; he adds a new item at the end of the list. In this example, this is equivalent to the result result = result + [a], but more efficient.

The documentation shows that approach 1 is more efficient. Why is this so?

+5
source share
3 answers

Although using .append requires a method call, it is actually a bit more efficient than using the extended assignment operator, += .

But there is another reason to use .append : you can do this when the list you are adding is not in the local scope, as it just calls the object method in the outer scope, while you cannot perform assignments for objects that are not included to the local area unless you declare them global (or nonlocal), which is usually best avoided.

Here is 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 a 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 of the reasons that test_iadd2 slower is because it needs to build a new [i] list in each loop.

FWIW, here is 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 was created 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) 
+9
source

 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 

The append method is faster because it does not use type conversion.

+1
source

The second is slower because it creates a new list (which is somewhat slower).

0
source

Source: https://habr.com/ru/post/1238265/


All Articles