Late-binding of function defaults (was Re: What is a function parameter =[] for?)

Steven D'Aprano steve at pearwood.info
Thu Nov 26 20:09:53 EST 2015


On Thu, 26 Nov 2015 12:23 pm, BartC wrote:

> On 26/11/2015 00:31, Steven D'Aprano wrote:

>> In 2015, it's hard to think of any non-obsolete, non-toy language which
>> doesn't treat functions as first-class values, including creating them on
>> the fly. Fortran and C perhaps.
> 
> It's funny then that the vast majority of top-level function definitions
> I see in Python (and import and class statements too) are decidedly
> static.

I can't be held responsible for what code you see.

It is probably true that many, maybe even a majority, of top-level
functions, and methods, are static. So what? That's not the point. The
point is that *more than that is possible*, so when you need the power, you
have it.

Any time you see a function decorator:

@decorate
def function(arg): ...


you're probably seeing a dynamically-generated function. The typical
decorator idiom looks like this:

def decorate(function):
    @functools.wraps(function)
    def inner(arg):
        do stuff
        call the original function
        return the result
    return inner


Lo and behold, the simple @decorate idiom usually hides a function created
on the fly. (That would be the inner function, which is called a closure.)

Any time you see a function factory, chances are good that it involves
dynamic generation of function objects.

Any time you see lambda, you are definitely seeing the dynamic generation of
function objects. If you do any sort of programming involving callbacks
(for example, some GUI toolkits, like Tkinter, make extensive use of
callbacks) you probably will use lambda a lot.

The equivalent to lambda, code blocks, is even more powerful in Ruby, and
Ruby programmers use code blocks *a lot*. So much so that I know at least
one Ruby programmer who considers Python almost unusably primitive and
crippled because it lacks code blocks.

You really should read Paul Graham's essay, "Beating the Averages", and
understand the Blub Paradox. (It's not really a paradox.) It might open
your eyes.

[quote]
As long as our hypothetical Blub programmer is looking down the power
continuum, he knows he's looking down. Languages less powerful than Blub
are obviously less powerful, because they're missing some feature he's used
to. But when our hypothetical Blub programmer looks in the other direction,
up the power continuum, he doesn't realize he's looking up. What he sees
are merely weird languages. He probably considers them about equivalent in
power to Blub, but with all this other hairy stuff thrown in as well. Blub
is good enough for him, because he thinks in Blub.
[end quote]

http://www.paulgraham.com/avg.html


We're all Blub programmers. But some of us are aware that we're Blub
programmers, and when we see "weird languages" with "hairy stuff", we don't
immediately dismiss the possibility that maybe that stuff is powerful and
useful, just because we've never found a use for it.



> The names are declared, but the names are rarely bound to anything else.
> Functions are just called the same boring way they are in C.
> 
> /They might as well be static definitions/.

Sure. And that's probably true for, oh, I don't know, I'll be generous, and
say that 95% of Python functions in use are of a straight-forward
pseudo-static form, using no dynamic features.

But it's the other 5% -- one in twenty -- that *shine*. They're the ones
that do things that you can't do in Pascal or C except with the greatest of
difficulty.

Back when I was an undergrad at uni, I did a course on computational
mathematics. At the time, Pascal was the language of choice at my uni, and
when it came to solving ODEs (Ordinary Differential Equations) we used a
package of Pascal programs to do so.

Because Pascal doesn't have first-class functions, you couldn't pass a
function as an argument to another function. So the ODE solvers used a
hard-coded function as the equation to be solved. So if you wanted to solve
a particular equation, you had to *edit the source code of the program*,
modifying that hard-coded function to match your equation, then re-compile,
and then run the code. You couldn't just type in the equation you wanted to
solve and run the code.

Of course Pascal is Turing Complete, and the solver could have been written
to work that way. It just would have been much, much, much harder.
Something that in a language like Python you can do almost without
thinking:

function(another_function, x, y, z)

would have required hundreds, perhaps thousands or tens of thousands of
lines to do in standard Pascal. You would have had to build up all this
infrastructure for dealing with functions that Python gives you for free.

There's a lot to like about Pascal, but gosh it sure is lacking in power. C
at least has function pointers, which is a step up from Pascal, but still
pretty weak and feeble. If functions are first class values in Python,
Ruby, Lisp and others, they're second class values in C and third class in
Pascal.




-- 
Steven




More information about the Python-list mailing list