An error that is easy to encounter when setting variables as default values in Python

  • 2020-05-05 11:23:24
  • OfStack

Consider the following code snippet:
 


def foo(numbers=[]):
  numbers.append(9)
  print numbers

Here, we define an list (empty by default), add a 9 to it and print it out.
 


>>> foo()
[9]
>>> foo(numbers=[1,2])
[1, 2, 9]
>>> foo(numbers=[1,2,3])
[1, 2, 3, 9]

Does it look all right? But when we call the foo function without entering the number parameter, the magic happens:
 


>>> foo() # first time, like before
[9]
>>> foo() # second time
[9, 9]
>>> foo() # third time...
[9, 9, 9]
>>> foo() # WHAT IS THIS BLACK MAGIC?!
[9, 9, 9, 9]

So, what's going on here? Our intuition tells us that no matter how many times we call the foo function without the number argument, the 9 here should be allocated to an empty list. This is wrong! In Python, the default value of a function is instantiated when the function is defined, not when it is called.

So we still ask, why is this default value given a different value when the function is called? Because Python stores this value every time you specify a default value for a function. If the default value is overridden when the function is called, the stored value is not used. When you do not override the default value, Python makes the default value refer to the stored value (numbers in this example). It does not copy the stored value to assign a value to the variable. This concept may be difficult for beginners to understand, so it can be understood as having two variables, one internal and one current runtime. The reality is that we have two variables to interact with with the same value, so if the value of numbers changes, it also changes the record of the initial value held in Python.

The solution:
 


def foo(numbers=None):
  if numbers is None:
    numbers = []
  numbers.append(9)
  print numbers

Usually, when people hear this, they ask another question about default values. Consider the following program:
 


def foo(count=0):
  count += 1
  print count

When we run it, the result is exactly what we expected:
 


>>> foo()
1
>>> foo()
1
>>> foo(2)
3
>>> foo(3)
4
>>> foo()
1

Why is that? The secret is not when the default value is assigned, but the default value itself. An integer is an immutable variable. Unlike the list type, integer variables cannot be changed during the execution of a function. When we execute count+=1, we do not change the original value of count. Instead, count points to a different value. However, when we implement numbers.append (9), we change the original list. Hence the result.

Here's another example of the same problem with default values in functions:
 


def print_now(now=time.time()):
  print now

As before, the value of time.time () is variable, so it will only be calculated at the time the function is defined, so no matter how many times it is called, it will return the same time -- the output time here is the time the program was interpreted by Python.


>>> print_now()
1373121487.91
>>> print_now()
1373121487.91
>>> print_now()
1373121487.91

* this problem and its solution are similar in Python 2.x and 3.x; the only difference in Python 3.x is that the print expression should be the way the function is called (print(numbers)).


Related articles: