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.