⚠️ Be careful with Python's default argument values ⚠️

Ciao ... †

I've recently been plagued by unintended behavior with Python's default argument values, so I'd like to share information here so I don't repeat the same mistakes. This environment is Python 3.7.7 and 3.8.3.

What is the default argument value?

Refers to dt = datetime.now () in the code below.

from datetime import datetime


def show_second(dt=datetime.now()):
    print(dt.second)

Python default argument value trap

I messed with the previous code to call the show_second function once and then call the show_second function again three seconds later.

import time
from datetime import datetime


def show_second(dt=datetime.now()):
    print(dt.second)

show_second()  #=> 23
time.sleep(3)
show_second()  #=> 23

Then what do you mean! Even though I slept for 3 seconds, the value printed when calling the show_second function for the second time was the same as 3 seconds ago ...! ?? Has time stopped? the world? ?? ?? Are you using a new stand? ?? ??

Python default argument value behavior

Well, when I read the Python documentation to see what this means, I found the following description.

The default argument value is evaluated from left to right when the function definition is executed. This means that the default argument expression is evaluated only once when the function is defined and the same "calculated" value is used for each call. from: https://docs.python.org/ja/3/reference/compound_stmts.html#function-definitions

In other words, the default argument value is evaluated once when the function is defined, the result is saved in the memory, and after that, when the default argument value is used no matter how many times the function is called, the evaluation result at the time of function definition is used. It seems that it is a mechanism.

Therefore, it is dangerous to use something like datetime.now () that gives a different result each time you call and / or something that requires real-time performance as the default argument value ⚠️

Similarly, it seems that you need to be careful when you specify a list or dictionary as the default argument value. (Usage and notes on default arguments in Python functions | note.nkmk.me)

Countermeasures for Python default argument values

Then, what to do is to set None to the default argument value, and if it is None, substitute the value that you originally wanted to set as the default argument value.

Below is the example code.

import time
from datetime import datetime


def show_second(dt=None):
    if dt is None:
        dt = datetime.now()
    print(dt.second)

show_second()  #=> 23
time.sleep(3)
show_second()  #=> 26

That's why I was talking about paying attention to Python's default argument values.

Recommended Posts

⚠️ Be careful with Python's default argument values ⚠️
Be careful with Python's append method
[Introduction to Udemy Python3 + Application] 51. Be careful with default arguments
Be careful with easy method references
(Note) Be careful with python argparse
Be careful when running CakePHP3 with PHP7.2
I wanted to be careful about the behavior of Python's default arguments
Be careful when working with gzip-compressed text files
Be careful when reading data with pandas (specify dtype)
Python default argument trap
[Python3] Be careful with removing character strings (strip, lstrip, rstrip)