1

I have just read an article: The 10 Most Common Mistakes That Python Developers Make. The problem #1 still puzzled me a lot. I will describe my problem with the codes below.

>>>def foo(bar=[]):
...    bar.append("baz")
...    return bar
>>> foo()
["baz"]
>>> foo()
["baz", "baz"]

It not works, the answer in the article says list is an multable type and the default value changes during the call.But when I try this one

def foo(bar=[]):
    if bar == []:bar=[]
...

it works, so what makes the difference?

2 Answers 2

4

Default arguments are created at function definition in Python. This means that when you provide a list as a default argument, that list will persist for all function calls in which that argument is not explicitly passed. In the second example, you are reassigning the argument to a different value. That creates a new list and will fix the problem. A more common solution to this "bug"/"feature" is to assign the default argument a value of None and then check it at the top of the function. That would look something like this:

def foo(bar=None):
    if bar is None:
        bar = []

However, the code that you wrote in the second example achieves the same effect.

Sign up to request clarification or add additional context in comments.

4 Comments

And what changes in first example after all, is the default argument 'bar' changes after the call?if so, why the default argument in the second example don't change?there is an append operation as well, why it cannot remember the change of bar, but the first one do?
When you assign to bar inside the function you are not changing the default value of bar in the function definition, you are only changing it in the local variables for the particular function call. It helps to understand that objects are pointers to a particular memory location. If id(obj1) and id(obj2) have the same value, then they are the same object. Look at id(bar) before and after assigning bar=[] and you will see that the id has changed.
So the default argument refer to a new RAM location after the append operation in the first example?
Oh, I stare at it for a long time and I finally catch the hang of it: in the second example the bar is edited in a new location, when I call it for the second time, it refers to an empty list.But in the first place, the bar was edited in place.yeah?
0

In your code you assign a new list to bar instead using the default argument. Now if you modify the new list the changes of course aren't shown in the default argument which is different object.

The typical solution is to assign None to the default argument and explicitly check it at the beginning of function:

def foo(bar=None):
    if bar is None:
        bar = []

Note that although it looks a lot like your version there are differences:

def foo(bar=[]):
    if bar == []:
        bar = []
    bar.append(1)

def foo2(bar=None):
    if bar is None:
        bar = []
    bar.append(1)

l = []
foo(l)
print l # []

l = []
foo2(l)
print l # [1]

3 Comments

So the change of bar happens after the function is called, after the append operation is finished or after the call is finished in the first example?
@DavidPage: In the first example bar is changed to refer to different list if it's value is [], no matter if the value is coming from default argument or from caller. Then the list is modified with append but since at that point it's different list than what was given as an argument caller won't see the changes.
Thanks, both of you helped me a lot.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.