Default Value
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Fri Jun 21 12:22:17 EDT 2013
On Thu, 20 Jun 2013 20:16:19 -0700, Rick Johnson wrote:
> On Thursday, June 20, 2013 7:57:28 PM UTC-5, Steven D'Aprano wrote:
>> On Thu, 20 Jun 2013 11:05:32 -0700, Rick Johnson wrote:
>> They [default argument values] have to be stored *somewhere*, and
>> Python chooses to store them as an attribute of the function object,
>> where they can be easily inspected, rather than by storing them inside
>> some undocumented and hidden location locked up in a binary blob.
>
> I like how that last sentence implicitly argues against an argument i
> never made, whilst explicitly supporting your argument.
You're overly defensive. I never said, or even suggested, that you argued
the opposite. I simply state a fact: any language with default arguments
must store the default argument somewhere. In a language with early
binding of default arguments, the *value itself* must be stored. In a
language with late binding, *the expression that generates that value*
(sometimes called a "thunk") must be stored.
The consequence of this fact is that if you wish to argue that functions
have no business storing state, then you're arguing that functions must
not have default arguments.
[...]
>> Even if Python chose late binding instead of early binding for function
>> defaults, the default would *still* need to be stored somewhere.
>
> No, if your passing in a symbol, then the object it points to must exist
> *somewhere*. OTOH if your passing in a literal, or an expression, then
> the subroutine will need to "store" the resulting object. Yes.
Passing in a symbol? I'm afraid I don't understand you. Do you mean a
name? Something like:
def func(x=y):
...
A single name, like 'y' above, is an expression. In any case, while the
name must exist at the time the function is defined, it does not need to
exist when the function is called:
py> y = 23
py> def func(x=y):
... return x
...
py> del y
py> func()
23
>> The only difference is that with early binding, the default value is
>> calculated once, and the object stored, while with late binding the
>> default value is re-calculated over and over again, every time it is
>> needed, and a piece of code that calculates the default value is
>> stored.
>
> And that is *ONLY* the case using the currently broken system.
No. You have failed to understand. ANY language that offers default
values for function parameters must operate under the same limitation.
The language must store either the default value itself, or some
executable expression that generates that default value.
Even if your language doesn't offer default values, all this means is
that the responsibility for doing this is moved away from the compiler to
you. For instance, if your languages offers an "is_undefined" function,
you can manually store the default value in a global variable and then
retrieve it when needed:
# Store function state in a global variable.
default := [];
def func(x):
if is_undefined(x):
{
x := default;
}
...
That suffers the *exact same gotcha* as Python if the default value is
mutable.
Or you can store the state in a callable class, what C++ calls a functor:
class MyClass:
# Store function state in a class member.
default := [];
def __call__(this, x):
if is_undefined(x):
{
x := this.default;
}
...
This too suffers the *exact same gotcha* as Python if the default is
mutable. Storing the function state as an instance member instead of a
class member doesn't save you; the same applies.
There are only three ways to avoid the mutable default gotcha:
* Prohibit default arguments, and don't provide an is_undefined
test. All function arguments *must* be supplied explicitly by
the caller, like in Pascal.
* Use late-binding and re-calculate the default every time it is
needed. This risks being expensive, and has its own share of
gotchas.
* If you can somehow determine which values are mutable and which
are not, you might allow default arguments only for immutable
values. Some restrictive languages can do this: Java, with it's
strongly enforced rules for "private" and "protected" members,
and compile-time checks, may be able to do something like this.
And purely functional languages like Haskell simply avoid the
issue by ensuring that *all* values are immutable. But for
Python, it is impossible.
--
Steven
More information about the Python-list
mailing list