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