Page 2 of 2
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:
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:
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:
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:
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:
if time1is None:
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.
|Sharpen Your Coding Skills|
Three Warehouses Puzzle
Talking through a problem is often a good way to see what is required for its solution. Reducing it in scale is another good strategy. But as programmers at the end of the day we need to code an algor [ ... ]