Python Puzzle - Where Did The Time Go?
Written by Mike James   
Article Index
Python Puzzle - Where Did The Time Go?
Solution

pythonquestion

Solution

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. 

Pattern

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:
        time1=time.time()
    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:
        time1=time.time()
    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:
        time1=time.time()
    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.

Banner

More Puzzles

C#
In-Place Or Operator Methods?

This particular C# puzzle is one of those that involves an error that no self respecting programmer would make... but are you so sure. If you use the DateTime class regularly then sure, you not only w [ ... ]


Sharpen Your Coding Skills
The Best Sub-Array Problem

At first glance this puzzle seems trivial, all you have to do is find a sub-array, in an array of numbers,  that sums to the largest value. It sounds almost too easy to need a solution, let alone [ ... ]


Sharpen Your Coding Skills
Towers Of Hanoi Mutants

Towers of Hanoi is a classic puzzle and is often used to illustrate the idea of recursion. Here you are challenged to find solutions to some variations, after first explaining the original version.


Other Articles

<ASIN:1871962587>