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 -- http://mail.python.org/mailman/listinfo/python-list