When are default arguments evaluated?

When you supply a default parameter value for a function, you do so by providing
an expression. This expression can be a simple literal value, or it can be a more complex function call. In order to actually use the default value that you provide, Python has to at some point evaluate that expression.

It's crucial, then, to have an appreciation of exactly when Python evaluates the default value expression. This will help you to avoid a common pitfall which frequently ensnares newcomers to Python. Let's examine this question closely using the Python standard library time module:

>>> import time

We can easily get the current time as a readable string by using the ctime() function of the time module:

>>> time.ctime()
'Sat Feb 13 16:06:29 2016'

Let's write a function which uses a value retrieved from ctime() as a default argument value:

>>> def show_default(arg=time.ctime()):
... print(arg)
...
>>> show_default()
Sat Feb 13 16:07:11 2016

So far so good, but notice what happens when you call show_default() again a few seconds later:

>>> show_default()
Sat Feb 13 16:07:11 2016

And again:

>>> show_default()
Sat Feb 13 16:07:11 2016

As you can see, the displayed time never progresses.

Recall how we said that def is a statement that when executed binds a function definition to a function name? Well, the default argument expressions are evaluated only once, when the def statement is executed. In many cases the default value is a simple immutable constant like and integer or a string, so this does not cause any problems. But it can be a confusing trap for the unwary that usually shows up when you use mutable collections like lists as argument defaults.

Let's take a closer look. Consider this function which uses an empty list as a default argument. It accepts a menu as a list of strings, appends the item "spam" to the list, and returns the modified menu:

>>> def add_spam(menu=[]):
... menu.append("spam")
... return menu
...

Let's create a simple breakfast of bacon and eggs:

>>> breakfast = ['bacon', 'eggs']

Naturally, we'll add spam to it:

>>> add_spam(breakfast)
['bacon', 'eggs', 'spam']

We'll do something similar for lunch:

>>> lunch = ['baked beans']
>>> add_spam(lunch)
['baked beans', 'spam']

Nothing unexpected so far. But look what happens when you rely on the default argument by not passing an existing menu:

>>> add_spam()
['spam']

When we append 'spam' to an empty menu we get just spam. This is probably still what you expected, but if we do that again we get two spams added to our menu:

>>> add_spam()
['spam', 'spam']

And three:

>>> add_spam()
['spam', 'spam', 'spam']

And four:

>>> add_spam()
['spam', 'spam', 'spam', 'spam']

What's happening here is this. First, the empty list used for the default argument is created exactly once, when the def statement is executed. This is a normal list like any other we've seen so far, and Python will use this exact list for the entire execution of your program.

The first time we actually use the default, then, we end up adding spam directly to the default list object. When we use the default a second time, we're using the same default list object — the one to which we just added spam – and we end up adding a second instance of "spam" to it. The third call adds a third spam, ad infinitum. Or perhaps ad nauseum.

The solution to this is straightforward, but perhaps not obvious: Always use immutable objects such as integers or strings for default values. Following this advice, we can solve this particular case by using the immutable None object as a sentinel:

>>> def add_spam(menu=None):
... if menu is None:
... menu = []
... menu.append('spam')
... return menu
...
>>> add_spam()
['spam']
>>> add_spam()
['spam']
>>> add_spam()
['spam']

Our add_spam() function works as expected.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset