ERJAN
ERJAN

Reputation: 24500

What happens when I reassign the mutable default argument inside a function?

I know this has been answered in highly active 'least astonishment' question, I modified code snippet slightly but still don't understand why it produces new empty list and adds 1 to it..

def foo(var=[]):
    if len(var) == 0:
        var = []
    print(var)
    var.append(1)
    return var

print(foo())
print(foo())

outputs:

[]
[1]
[]
[1]

My expected logic of this python snippet:

But it does still go to if clause and init var to [] every time.. why?

However, if I remove the if clause:

def foo(var=[]):

    print(var)
    var.append(1)
    return var


print(foo())
print(foo())

It does append "1" to var every time and var grows:

[1]

[1,1]

So the if clause is entered and checked for validity.. confused about the execution..

Upvotes: 1

Views: 88

Answers (3)

Hymns For Disco
Hymns For Disco

Reputation: 8395

var=[] on line 1 gets evaluated once when the function is defined

var=[] on line 3 gets evaluated every time you pass a var with len(var) == 0, for instance when you pass no args and the default is used.

This means that the [] on line 3 is a new list every time the function is called, as that line of code is executed every time the function is called.

Upvotes: 1

Data_Is_Everything
Data_Is_Everything

Reputation: 2016

alright, so the two things:

  1. var in your function definition is assigned to an empty list i.e [] that is good,
  2. var in the if statement is again being re-assigned but is the same thing, an empty list, this you don't really need as it's preventing you from achieving [1,1]

this may help clear things of why you're "expecting" a var = [1,1] the first time around:

 def foo(var=[]):
    if len(var) == 0:
       print(var)

    var.append(1)
    return var

print(foo()) # returns [],[1]

print(foo()) # returns  [1,1] this list appended another 1 in because when you ran this instance of foo, this list was not empty anymore rather filled with 1 and bypassed the if statement and appended another 1 resulting to [1,1]

thus, you don't really need that var = [] in the if statement as it confuses the next steps as to what you want to achieve...

hope that helps somewhat :)

Upvotes: 1

cs95
cs95

Reputation: 402553

Let's rewrite your example so that we make some change to the argument before reassigning:

In [262]: def foo(var=[]): 
     ...:     var.append(1)   # 1
     ...:     print(var)      # 2
     ...:     var = []        # 3
     ...:     return var      # 4
     ...:                                                                            

In [263]: foo()                                                                      
[1]
Out[263]: []

In [264]: foo()                                                                      
[1, 1]  # reflects the append from the previous call
Out[264]: []

The append step in line 1 mutates the default argument list. The reassignment step in line 3 simply reassigns the variable var to a new list (a completely different object), that's what you return.

You'll see each subsequent call modifies the default argument list, it's still there but you just don't see it because you lose the reference to it when you reassign.

I recommend reading this article by Ned Batchelder.

Upvotes: 2

Related Questions