Modifying the default argument of function

M

Hi everybody,

A friend of mine asked me a question about the following code:

Code:
def f(x=[2,3]):
x.append(1)
return x

print(f())
print(f())
print(f())

The results are [2, 3, 1], [2, 3, 1, 1] and [2, 3, 1, 1, 1].

The function acts as if there were a global variable x, but the call of
x results in an error (undefined variable). I don't understand why the
successive calls of f() don't return the same value: indeed, I thought
that [2,3] was the default argument of the function f, thus I expected
the three calls of f() to be exactly equivalent.

I'm don't know much about python, does anybody have a simple explanation
please?
 
C

Chris Angelico

The function acts as if there were a global variable x, but the call of x
results in an error (undefined variable). I don't understand why the
successive calls of f() don't return the same value: indeed, I thought that
[2,3] was the default argument of the function f, thus I expected the three
calls of f() to be exactly equivalent.

In a sense, there is. The default for the argument is simply an object
like any other, and it's stored in one place.

For cases where you want a mutable default that is "reset" every time,
the most common idiom is this:

def f(x=None):
if x is None: x=[2,3]
x.append(1)
return x

That will create a new list every time, with the same initial contents.

ChrisA
 
S

Steve Jones

Hi everybody,

A friend of mine asked me a question about the following code:

Code:
def f(x=[2,3]):
x.append(1)
return x

print(f())
print(f())
print(f())

The results are [2, 3, 1], [2, 3, 1, 1] and [2, 3, 1, 1, 1].

The function acts as if there were a global variable x, but the call of
x results in an error (undefined variable). I don't understand why the
successive calls of f() don't return the same value: indeed, I thought
that [2,3] was the default argument of the function f, thus I expected
the three calls of f() to be exactly equivalent.

I'm don't know much about python, does anybody have a simple explanation
please?

x is assigned to the list [2, 3] at the time the function is created not when the function is called, meaning that there's only ever 1 list created. When you call x.append this list is modified and the next time the function is called x still refers to this modified list.
 
E

emile

Function defs with mutable arguments hold a reference to the mutable
container such that all invocations access the same changeable container.

To get separate mutable default arguments, use:

def f(x=None):
if x is None: x=[2,3]

Emile
 
M

Mû

Le 21/01/2014 20:19, Chris Angelico a écrit :
The function acts as if there were a global variable x, but the call of x
results in an error (undefined variable). I don't understand why the
successive calls of f() don't return the same value: indeed, I thought that
[2,3] was the default argument of the function f, thus I expected the three
calls of f() to be exactly equivalent.

In a sense, there is. The default for the argument is simply an object
like any other, and it's stored in one place.

For cases where you want a mutable default that is "reset" every time,
the most common idiom is this:

def f(x=None):
if x is None: x=[2,3]
x.append(1)
return x

That will create a new list every time, with the same initial contents.

ChrisA

Thank you, thanks everybody,

These were clear and quick answers to my problem. I did not think of
this possibility: the default argument is created once, but accessible
only by the function, therefore is not a global variable, whereas it
looks like if it were at first glance.
 
C

Chris Angelico

These were clear and quick answers to my problem. I did not think of this
possibility: the default argument is created once, but accessible only by
the function, therefore is not a global variable, whereas it looks like if
it were at first glance.

You can actually poke at the function a bit and see what's happening.
Try this in the interactive interpreter:
x.append(1)
return x
f() [2, 3, 1]
f() [2, 3, 1, 1]
f.__defaults__
([2, 3, 1, 1],)

The __defaults__ attribute of a function is a tuple of its parameter
defaults. You can easily see there that the list has changed as you
changed it in the function. You could check it with id() or is, too:
id(f.__defaults__[0]) 24529576
id(f()) 24529576
f() is f.__defaults__[0]
True

ChrisA
 
A

Asaf Las

These were clear and quick answers to my problem. I did not think of this
possibility: the default argument is created once, but accessible only by
the function, therefore is not a global variable, whereas it looks likeif
it were at first glance.
You can actually poke at the function a bit and see what's happening.
Try this in the interactive interpreter:
x.append(1)
return x
f() [2, 3, 1]
f() [2, 3, 1, 1]
f.__defaults__
([2, 3, 1, 1],)

The __defaults__ attribute of a function is a tuple of its parameter
defaults. You can easily see there that the list has changed as you
changed it in the function. You could check it with id() or is, too:
id(f.__defaults__[0]) 24529576
id(f()) 24529576
f() is f.__defaults__[0]
True
ChrisA

that reminds me C's static :)

def func(y, x = [1]):
if y != 1 :
func.__defaults__[0][0] = y
print(func.__defaults__[0])

func(0)
func(2)
func(1)

[0]
[2]
[2]

p.s. Mu, thanks for question!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top