Function arguments in Python
Common pitfalls in Python functions
The Mysterious behavior of function arguments in Python.
You may have already seen some of the odd ways arguments in Python functions behave. Let’s look at those weird behaviors and try to explain them. All my examples are run on Python 3.8, but they are not specific to this version of Python and should run normally in other versions without any problems.
Not being able to change argument reference
Let’s demonstrate this with an example
a = 5
def change(x):
x = 10
print(x)
change(a) # expect a to be 10
print(a) # but a is still 5
The value of the passed-in argument does not change
What’s happening here is that a=5
creates a new int
object of the value 5 and assigns it to the named reference a
.
- The call to
change(a)
makesx
refer toa
. - But within the function body the line
x = 10
makesx
point to a newint
object of the value 10. At this pointx
has no idea whata
is. - Thus the
change
function only changes references for its own parameterx
and not of the value that was passed in.
This behavior occurs because variables in Python are really named references to objects and not the objects themselves. The box holding values does not work here.
If you really need to change the value of an immutable object just return the new value from the function. Like this,
# Returning values from function
def change(x):
x = 10
return x
Persisting values in default arguments
Here’s another example that you may already have seen before,
>>> def add_salt(food=[]):
... food.append('salt')
... print(food)
...
>>> salad = ['cucumber', 'tomatoes']
>>> add_salt(salad)
['cucumber', 'tomatoes', 'salt']
Everything’s fine, right? But see what happens when the add_salt
function is called without any arguments.
>>> add_salt()
['salt']
>>> add_salt()
['salt', 'salt']
>>> add_salt()
['salt', 'salt', 'salt']
This is because of how default arguments in Python functions are evaluated. They are evaluated only once when the interpreter first encounters them. When they are first encountered by the interpreter? When the interpreter first encounters the def
statement, it binds the name add_salt
to a function object and evaluates its parameters, here food=[]
. When you call add_salt
without any arguments for the first time, an empty list
object is created and bound to the named reference food
. Next time you call add_salt
without any argument, 'salt'
is added to that empty list that food
refers to because it is not evaluated again.
This is a common problem in python and to avoid this, do not use any mutable object as the default argument of a function definition.
Originally published at https://blog.shaphil.me on April 25, 2020.