This is a memo of O'Reilly Japan's book effective python. https://www.oreilly.co.jp/books/9784873117560/ P47~50
For example, when defining a function that outputs login time and logging message at the same time
from datetime import datetime
from time import sleep
def log(massage, when=datetime.now()):
print('%s: %s' % (when, massage))
Now, if you give a message to the log () function as an argument, that time will be output at the same time as the message ... When I actually try it
log('Hi there!')
sleep(1)
log('Hi there!')
>>>
2016-06-14 00:22:04.668549: Hi there!
2016-06-14 00:22:04.668549: Hi there!
The time is the same even though I put sleep () for 1s. Furthermore, this time is not the time when the message is input, but the time when the log () function is defined. Why is this happening?
That is, datetime.now () is evaluated only for the moment when the function log () is defined, and all the values after that are the same. ** To resolve this, set the default argument to None **
def log2(massage, when=None):
"""Lof a massage with a timestamp.
Args:
massage: Massage to print.
when: datetime of when the massage occurred.
Dafaults to the present time.
"""
when = datetime.now() if when is None else when
print('%s: %s' % (when, massage))
The None attribute is given to the argument when in advance, and when is defined and called each time in the function. At this time, it is easier to understand if you write the behavior in the documentation.
log2('Hi there!')
sleep(1)
log2('Hi there!')
>>>
2016-06-14 00:34:21.793073: Hi there!
2016-06-14 00:34:22.794493: Hi there!
The time stamp is now correct.
Another example! For functions that load JSON data
def decode(data, default={}):
try:
return json.loads(data)
except ValueError:
return default
Whenever I try to call the decoded data as JSON data using the decode () function
foo = decode('bad data')
foo ['sruff'] = 5
bar = decode('also bad')
bar['meep'] = 1
print('Foo:', foo)
print('Bar:', bar)
>>>
Foo: {'sruff': 5, 'meep': 1}
Bar: {'sruff': 5, 'meep': 1}
In this case, it is assumed that each of Foo and Bar will be a dictionary of one key and value, but like the above dynamic argument, the default dictionary will be fixed when decode () is defined. As a result, even if you call decode () multiple times, the dictionary inside will always inherit one.
This is also solved by putting None in the default (behavior is written in the documentation)
def decode2(data, default=None):
"""Load JSON data from a string.
Args:
data: JSON data to decode
default: Value to return if decoding fails.
Dafaults to an empty dictionary.
"""
if default is None:
default = {}
try:
return json.loads(data)
except ValueError:
return default
foo = decode2('bad data')
foo ['sruff'] = 5
bar = decode2('also bad')
bar['meep'] = 1
print('Foo:', foo)
print('Bar:', bar)
>>>
Foo: {'sruff': 5}
Bar: {'meep': 1}
** Summary ** --Function defaults are read only once --Dynamic argument is None