Default Value

Steven D'Aprano steve+comp.lang.python at pearwood.info
Thu Jun 20 20:57:28 EDT 2013


On Thu, 20 Jun 2013 11:05:32 -0700, Rick Johnson wrote:

> On Thursday, June 20, 2013 10:38:34 AM UTC-5, Chris Angelico wrote:
>> Function defaults in Python, being implemented as attributes on the
>> function object, are very similar in nature to static variables in C.
> 
> Oh wait a minute. i think it's becoming clear to me now!

If only that were true, but I fear that you're just looking for an 
argument.


> Python functions are objects that take arguments, of which (the
> arguments) are then converted to attributes of the function object.

Arguments in general are *not* converted to attributes. If you call a 
function func(x=1, y=2), arguments 1 and 2 are not stored as attributes 
anywhere. That would be silly, since 1 and 2 become local variables and 
there is no point in storing them for later use since they don't get used 
later.

Only default values for parameters are stored for later use. They 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.

Even if Python chose late binding instead of early binding for function 
defaults, the default would *still* need to be stored somewhere. 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.


> Ah-Ha! Urm, but wait! We already have a method to define Objects. Heck,
> I can even create my own callable objects if i want!
[snip]

Yes, you can create callable objects by defining a __call__ method. 
That's okay. It is pure slander, invented by Perl programmers, that 
Python gives you "only one way to do it". Python has functions for 99% of 
your subroutine needs, and for more complex cases where your subroutine 
needs to store permanent data and make them available to the caller, you 
have class-based callables.

But really, using a class-based callable is a PITA. You have to define a 
class, write an __init__ method, write a __call__ method, instantiate the 
class, store the instance, and call it. 99% of the time a function will 
do the job more easily, especially now that Python supports closures.


[...]
> But then, i can even do it WITHOUT creating an object definition:
> 
>   py> mutable = []
>   py> def expandMutable(arg):
>   ...     mutable.append(arg)
>   ...
[...]

The biggest, most obvious problem with the expandMutable approach is that 
it relies on a global variable. I find it implausible that you, an 
experienced programmer, is unaware of the dangers of global variables. 
"Global variables considered harmful" has been a well-known principle of 
programming dating back to the 1970s.


> ANY of those approaches are much less confusing than the current flaw

Confusing for whom? Beginners, who don't know the first thing about 
Python? Programmers who are experienced with some other language but have 
no clue about what these weird __init__ and __call__ methods do? 
Programmers with 40 years experience who don't know anything about object-
oriented code? People who have been immersed in Python coding for 15 
years? Every feature is confusing to *some* people and not others.


> and do not violate the least astonishment law..

That's a misuse of the Principle of Least Astonishment. Not just a 
misuse, but in fact you've got it backwards: late binding of default 
variables would violate the principle of least astonishment.

Python functions are created *once*, when defined. The cost of building 
the function -- compiling the source code to byte code, assembling the 
pieces into a function object, binding it to a name -- happens once and 
once only, not every time you call the function. So it is reasonable to 
expect that since the function is defined once, so are any default 
arguments.

There are two obvious arguments against late binding: efficiency, and 
consistency. Late binding is less efficient: if the code that defines the 
default is expensive, you have to pay that cost once, that can't be 
avoiding. But late binding makes you pay it again and again and again:

def f(arg, value=time.sleep(100)+5):  # simulate an expensive default
    ...

I have no doubt that if Python recalculated the default every time, you 
would be arguing that this is surprising and painful and that Python 
ought to calculate the value once and cache it somewhere.

The other gotcha with late binding is that because the default is 
recalculated each time, it can surprise you by changing unexpectedly:

def f(arg, value=x+5):
    ...

If the value of x changes, so does the value of the default. This may 
surprise you. (It would certainly surprise me.)


> I'm quite Okay with
> Python functions being first class objects, however, i am not okay with
> violating the fundamental nature of subroutines, 

In what way to Python functions violate the fundamental nature of 
subroutines?


> especially when that
> violation can offer no clear and arguable benefits and is in fact
> unreasonably esoteric in nature.

Early binding offers clear benefits:

- consistency: the default cannot mysteriously change value due 
  to an unrelated change;

- efficiency: the default is calculated once, not over and over;

- it's easy to get late binding semantics starting with early 
  binding, but more difficult to go the other way.

- simplicity of implementation: late binding requires that 
  Python store, not the result of the expression, but the 
  expression itself (together with a context) for later 
  evaluation; early binding simply requires that the expression
  is evaluated at runtime, like any other expression.


That's not to say that there are no arguments in favour of late binding. 
But on balance, despite the odd sharp corner, I believe that early 
binding's benefits far outweigh its gotchas.



-- 
Steven



More information about the Python-list mailing list