What is the difference between “append” and “+” in

2020-08-22 06:26发布

问题:

I don't know what is difference between f() and g().

In function f, list L is accumulated whenever function is called..

but in function g, it does not..

def f(a, L=[]):
    L.append([2]);
    print(L);

def g(a, L=[]):
    L = L+[2];
    print(L);

print("f:")
f(1) # [[2]]
f(2) # [[2], [2]]
f(3) # [[2], [2], [2]]

print("g:")
g(1) # [2]
g(2) # [2]
g(3) # [2]

回答1:

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 []
    ...


回答2:

In first function you update existing list, when you append to the end of list new values.

In second function, you just reassign value to list, which means that you didn't add values that list, but just, lets say, recreate it with same value each time function called.

In both cases, you create local variable for function, which appended to that function clouser. So, in both cases you have local variable, which live in function scope. When you run your function 1-st time, in case of default value of variable, it will be memorized, and later, in case of executing function again, it will not be reinitialized by function, but would be taken from function scope.

For example, try to update code of g function to next one:

def g(a, L=[]):
    L += [2]
    print(L)

There you will see, that L accumulated too, as in f function, because here you not reassign the value, but update existing variable.



回答3:

Append adds the element to the end of the list. So if the list initially is L=[], then L.append([2]) will append a single element list to the original list L. If you again do L.append([3]), it will append another single element list to the end of original list L. Your list L is now actually list of lists.

+ is the concatenation operator. It joins two lists just like it joins two strings. Also, keep in mind that the + operator will always return a new list with elements joined. This is the reason why your list does not grow when you use the + operator. If you just want to mutate the existing list, you can use the extend function. The following answer and comments will make it more clear to you:

Difference between append vs. extend list methods in Python

Hope that helps.

Edit: To show how + returns a new list every time, here is the modified code that prints the id of the list everytime it prints the list:

def f(a, L=[]):
    L.append([2]);
    print(L);
    print(id(L))

def g(a, L=[]):
    #The following statement will print the id of default L
    print(id(L))
    L = L+[2];
    #The following statement will print the id of a new L stored in a diff location. The default L isnt modified.
    print(L);
    print(id(L))

f:
[[2]]
2354851792712
[[2], [2]]
2354851792712
[[2], [2], [2]]
2354851792712
g:
2354851792968
[2]
2354851791944
2354851792968
[2]
2354851791816
2354851792968
[2]
2354851792328

As you see above, every time you append, you use the same default list(2354851792712). Hence your list grows. On the other hand, using the + returns a new list and stores it in a new location, instead of modifying the default L location(2354851792968). Hence, it does not grow.



回答4:

list.append(element)

adds the "element" to the list. It doesn't discriminate based on data type of the "element".

The concatenation operator '+' although seems to perform the same operation, it concatenates only the same data types. Thus, you can concatenate only lists to lists. You can't have list1+"string". But you can have list1+["string1"], list1.append("string1") and list1.append(["string1"]).

Contrary to common belief, in python, lists are not immutable (mutable). That means, the same object can be edited over and over again without changing its reference. However, tuples and strings are immutable. That means, a new object is created whenever you edit a tuple or a string. The old string/tuple is then discarded by the garbage collection system.



回答5:

You are using a mutable default value. The first time you call f() or g() the interpreter creates an persistent list. With append in f() you are modifying this list. The + operator in g()causes the creation of a new list object, therefore the persistent list object created for the default value remains untouched. Use id(L) to print the reference to the objects to get a deeper insight.

Using mutable default values is considered an anti-pattern, since they can cause exactly this unexpected behavior (Source).

Update

Try running this code and you will see that L has a new id after assigning the result of the + operation and does not "point" to the persistent default value object anymore (it stayed unmodified).

def a(new_element, L=[]):
    print(id(L))
    L = L + [new_element]
    print(id(L))

a(1)


回答6:

This explains the first part of your question very well: https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html

So because you have a mutable default argument, python will create it just ones and will pass it to every new function call.

This explains the behaviour of function f. For function g, you have to consider that the + operator creates a new list which will be bound to the name L. The original default list, which will be passed to every function call however will not be touched (in function f you change the object itself).

Edit: Found this Link: http://effbot.org/zone/default-values.htm This explain the behaviour really well. So def is an executable and that's why default arguments are calculated in the definition of a function. Default arguments are then stored in function.func_defaults. During a function call the default parameter is passed from function.func_defaults to your local name L.