functions, list, default parameters
Steven D'Aprano
steve at REMOVE-THIS-cybersource.com.au
Fri Nov 5 04:58:19 EDT 2010
On Thu, 04 Nov 2010 22:34:07 +0000, Mark Wooding wrote:
> (Based on experience with other languages, I suspect that evaluating the
> default expression afresh for each call where it's needed would be more
> useful; but it's way too late to change that now.)
Let's call the two strategies:
defaults initialise on function definition (DID)
defaults initialise on function call (DIC)
I claim that when designing a general purpose language, DID (Python's
existing behaviour) is better than DIC:
(1) most function defaults only need to be set once;
(2) it's easy to get DIC if the language uses DID, but hard the other way;
(3) DIC is needless wasteful for common function definitions;
(4) DIC can lead to surprising bugs.
That last isn't a big concern, since DID can lead to surprising bugs as
well *wink*, but it's worth mentioning the failure mode.
#1 Most default values are things like True, False, None, integer or
string literals. Since they're literals, they will never change, so you
only need to set them once.
#2 It's easy to get default values to initialise on function call in a
language that uses initialisation on function definition semantics: just
move the initialisation into the function body. Python has a particularly
short and simple idiom for it:
def f(x=None):
if x is None:
x = some_expression()
But if the situations were reversed, it's hard to get the DID semantics:
def f(x=None):
if x is None:
global _f_default_arg
try:
x = _f_default_arg
except NameError:
_f_default_arg = x = default_calculation()
#3 Re-initialising default values is wasteful for many functions, perhaps
the majority of them. (Of course, if you *need* DIC semantics, it isn't
wasteful, but I'm talking about the situations where you don't care
either way.) In current Python, nobody would write code like this:
def f(x=None, y=None, z=None):
if x is None: x = 1
if y is None: y = 2
if z is None: z = 3
but that's what the DIC semantics effectively does. When you need it,
it's useful, but most of the time it's just a performance hit for no good
reason. A smart compiler would factor out the assignment to a constant
and do it once, when the function were defined -- which is just what DID
semantics are.
If you're unconvinced about this being a potential performance hit,
consider:
def expensive_function():
time.sleep(30) # simulate a lot of computation
return 1.23456789
def f(x=expensive_function()):
...
What would you prefer, the default value to be calculated once, or every
time you called f()?
#4 Just as DID leads to surprising behaviour with mutable defaults, so
DIC can lead to surprising behaviour:
def f(x=expression):
do_something_with(x)
If expression is anything except a literal, it could be changed after f
is defined but before it is called. If so, then f() will change it's
behaviour.
--
Steven
More information about the Python-list
mailing list