Python Append() VS. + Operator on Lists, Why Do These Give Different Results

Python append() vs. + operator on lists, why do these give different results?

To explain "why":

The + operation adds the array elements to the original array. The array.append operation inserts the array (or any object) into the end of the original array, which results in a reference to self in that spot (hence the infinite recursion in your case with lists, though with arrays, you'd receive a type error).

The difference here is that the + operation acts specific when you add an array (it's overloaded like others, see this chapter on sequences) by concatenating the element. The append-method however does literally what you ask: append the object on the right-hand side that you give it (the array or any other object), instead of taking its elements.

An alternative

Use extend() if you want to use a function that acts similar to the + operator (as others have shown here as well). It's not wise to do the opposite: to try to mimic append with the + operator for lists (see my earlier link on why). More on lists below:

Lists

[edit] Several commenters have suggested that the question is about lists and not about arrays. The question has changed, though I should've included this earlier.

Most of the above about arrays also applies to lists:

  • The + operator concatenates two lists together. The operator will return a new list object.
  • List.append does not append one list with another, but appends a single object (which here is a list) at the end of your current list. Adding c to itself, therefore, leads to infinite recursion.
  • As with arrays, you can use List.extend to add extend a list with another list (or iterable). This will change your current list in situ, as opposed to +, which returns a new list.

Little history

For fun, a little history: the birth of the array module in Python in February 1993. it might surprise you, but arrays were added way after sequences and lists came into existence.

In Python, what is the difference between .append() and += [] ?

For your case the only difference is performance: append is twice as fast.

Python 3.0 (r30:67507, Dec  3 2008, 20:14:27) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import timeit
>>> timeit.Timer('s.append("something")', 's = []').timeit()
0.20177424499999999
>>> timeit.Timer('s += ["something"]', 's = []').timeit()
0.41192320500000079

Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import timeit
>>> timeit.Timer('s.append("something")', 's = []').timeit()
0.23079359499999999
>>> timeit.Timer('s += ["something"]', 's = []').timeit()
0.44208112500000141

In general case append will add one item to the list, while += will copy all elements of right-hand-side list into the left-hand-side list.

Update: perf analysis

Comparing bytecodes we can assume that append version wastes cycles in LOAD_ATTR + CALL_FUNCTION, and += version -- in BUILD_LIST. Apparently BUILD_LIST outweighs LOAD_ATTR + CALL_FUNCTION.

>>> import dis
>>> dis.dis(compile("s = []; s.append('spam')", '', 'exec'))
1 0 BUILD_LIST 0
3 STORE_NAME 0 (s)
6 LOAD_NAME 0 (s)
9 LOAD_ATTR 1 (append)
12 LOAD_CONST 0 ('spam')
15 CALL_FUNCTION 1
18 POP_TOP
19 LOAD_CONST 1 (None)
22 RETURN_VALUE
>>> dis.dis(compile("s = []; s += ['spam']", '', 'exec'))
1 0 BUILD_LIST 0
3 STORE_NAME 0 (s)
6 LOAD_NAME 0 (s)
9 LOAD_CONST 0 ('spam')
12 BUILD_LIST 1
15 INPLACE_ADD
16 STORE_NAME 0 (s)
19 LOAD_CONST 1 (None)
22 RETURN_VALUE

We can improve performance even more by removing LOAD_ATTR overhead:

>>> timeit.Timer('a("something")', 's = []; a = s.append').timeit()
0.15924410999923566

Is a Python list's += operator equivalent to append() or extend()?

In python += on a list is equivalent to extend method on that list.

What is the difference between Python's list methods append and extend?

append appends a specified object at the end of the list:

>>> x = [1, 2, 3]
>>> x.append([4, 5])
>>> print(x)
[1, 2, 3, [4, 5]]

extend extends the list by appending elements from the specified iterable:

>>> x = [1, 2, 3]
>>> x.extend([4, 5])
>>> print(x)
[1, 2, 3, 4, 5]

Appending to list using append method and + operator produces different results in Python?

the difference is that lst.append(item) appends an item to the existing list (in-place) and returns None while the + operator (__add__) returns the result of the concatenation (a new list).

so when you call check( path.append(i), item, slns ) you do modify the list path but you effectively call check(None , item, slns ) instead of the new list as the first argument.

What is the difference between append and + in python?

The strange behavior you can see with your example is not due to the difference between the + operator and the append function.

It is due to the fact that you assigned a list as a default value to a function parameter and you assign the result of a concatenation in a local variable in g.


That is not so widely known but defining a list (or a dict or an object) as a parameter default value is probably not what you want. When you do so, the list used as default value for the parameter is shared across all function calls.

So for the f function:

  1. you call it a first time without the L parameter, the default value ([]) is taken and appended with 2. So it becomes [2]
  2. you call it a second time without the L parameter, the default value is taken but it is now [2]. 2 is appended again and it becomes [2, 2] which is now the default value of your function.

for the g function:

  • as for the f function, the default value is an empty list, but this list is never modified. Actually the result of L + [2] is affected to a local variable L and the default value of the function remains unchanged. So when you call the function again, the default value is always [].
  • As Klimenkomud said in his answer using the += operator instead of a concatenation and an assignation actually modifies the default value of the L parameter. If you do so, the g function will behave like the f function.

As a conclusion, using an empty list as a parameter default value is almost never what you want. Instead, you probably want this:

def f(a, L=None):
L = L or []
...

Why do these two append methods produce different results?

Function second_append always creates a new local list for you each time it is called.

Why does not the + operator change a list while .append() does?

So in a function the adding a 6 to the set doesn't show but it does when not in a function?

No, that is not what happens.

What happens is that, when you execute mylist = mylist + [6], you are effectively creating an entirely new list and putting it in the local mylist variable. This mylist variable will vanish after the execution of the function and the newly created list will vanish as well.

OTOH when you execute mylist.append(6) you do not create a new list. You get the list already in the mylist variable and add a new element to this same list. The result is that the list (which is pointed by list2 too) will be altered itself. The mylist variable will vanish again, but in tis case you altered the original list.

Let us see if a more visual explanation can help you :)

What happens when you call proc()

When you write list1 = [1, 2, 3, 4, 5] you are creating a new list object (at the right side of the equals sign) and creating a new variable, list1, which will point to this object.

Creating new list instance and global variable

Then, when you call proc(), you create another new variable, mylist, and since you pass list1 as parameter, mylist will point to the same object:

Calling method creates local variable

However, the operation mylist + [6] creates a whole new list object whose contents are the contents of the object pointed by mylist plus the content of the following list object - that is, [6]. Since you attribute this new object to mylist, our scenario changes a bit and mylist does not point to the same object pointed by list1 anymore:

mylist points to new list object

What I have not said is that mylist is a local variable: it will disappear after the end of the proc() function. So, when the proc() execution ended, the mylist is gone:

mylist is gone

Since no other variable points to the object generated by mylist + [6], it will disappear, too (since the garbage collector* will collect it):

GC collects the list

Note that, in the end, the object pointed by list1 is not changed.

What happens when you call proc2()

Everything changes when you call proc2(). At first, it is the same thing: you create a list...

Creating new list instance and global variable

...and pass it as a parameter to a function, which will generate a local variable:

Calling method creates local variable

However, instead of using the + concatenation operator, which generates a new list, you apply the append() method to the existing list. The append() method does not create a new object; instead, it _changes the existing one:

Appending a value to a list

After the end of the function, the local variable will disappear, but the original object pointed by it and by list1 will be already altered:

It is still altered

Since it is still pointed by list1, the original list is not destroyed.

EDIT: if you want to take a look at all this stuff happening before your eyes just go to this radically amazing simulator:

Sample Image

* If you do not know what is garbage collector... well, you will discover soon after understanding your own question.



Related Topics



Leave a reply



Submit