Python Puzzle - Where Did The Time Go?
Written by Mike James   
Thursday, 09 May 2019
Article Index
Python Puzzle - Where Did The Time Go?



OK - the problem is nothing to do with time keeping!

The same behavior could be demonstrated if almost any function was used to supply a default value in the same way. There are even problems if the default value is any mutable datatype, but that is another puzzle. 

The reason for the difficulty is that the function defaults are computed at the time the function is defined - not when it is called. 

So the default value for time1 is set when the Python interpreter reaches the definition of the function; not when the program calls the function.

This has two effects. The first is that the default value isn't set to the current time but the time when the function is defined. The second is that the apparent current time never changes. 

If you don't believe me try:

print( timediff(0))
print( timediff(0))
print( timediff(0))
print( timediff(0))

and you will see the same time value printed four times. 

This behavior often seems very strange to programmers familiar with other languages but most compiled languages have rules for what sorts of expressions you can use as default values and initial values.  These rules tend to come down to the simple requirement that the expression has to be fully determined at compile time. For Python compile time translates to definition time - hence the behavior. 

To be clear - the time.time() function is only called when timediff is defined i.e. when the Python interpreter reaches:

def timediff(time2,time1=time.time()):
    return time2-time1

When this happens the value of the expression i.e. the function is stored and used as the default value for time1 every time timediff is called without a specified value for time1. 

This is not a bug it is how Python 2 and 3 are supposed to behave. 


So what is the solution?

The main thing you need to be aware of is that initialization and default values aren't always determined at run time.

in this specific case you could simply revert to an earlier version of the function and avoid the use of default values:

def timediff(time2,time1):
    if time1==0:
    return time2-time1

This always works, but it isn't as convenient to use because you always have to specify a value for time1 even if it is a dummy. 

A better, and slightly more subtle, solution makes use of the default value as a flag to detect when the parameter needs dynamic initialization. 

For example a simple change to the previous version:

def timediff(time2,time1=0):
    if time1==0:
    return time2-time1

Now you can call timediff with time1 not specified and have a dynamic default value set at run time. 

Of course this depends on zero being a suitable flag for the missing argument - i.e. zero isn't going to be a valid time. More generally you can use None to indicate that a value is missing. For example:

def timediff(time2,time1=None):
    if time1is None:
    return time2-time1

You can always use None as a flag for a missing argument that needs dynamic initialization. 

The rule is:

  • always do dynamic defaults inside the function so that they are applied at run time. 

If you think that the troubles of default values are now all solved, the bad news is that there are other ways things can good wrong in what seem like very odd ways. But, as already mentioned, this is another puzzle. 

  • Mike James is the author of Programmer's Python: Everything is an Object published by I/O Press as part of the  I Programmer Library. With the subtitle "Something Completely Different" this is for those who want to understand the deeper logic in the approach that Python 3 takes to classes and objects.


More Puzzles

Value Or Reference? A C# Puzzle

The difference between a value and a reference type is very clear to most C# programmers, but it can be a shock when a simple piece of code that seems to do exactly what you want has a surprise in sto [ ... ]

Sharpen Your Coding Skills
The Post Production Problem

Joe Celko has posed another puzzle that requires you to think like a programmer. This one is all about Post tag machines, which have absolutely nothing to do with mail of any type but a lot to do with [ ... ]

Where Did The Logic Go?

Logic never goes wrong but in this case a simple refactoring of an if block seem to give a different result, even though it does exactly the same thing. Or does it? This Programmer Puzzle explores a g [ ... ]

Other Articles


Last Updated ( Friday, 10 May 2019 )