Re: lambda constructions

Guido.van.Rossum@cwi.nl
Mon, 30 Jan 1995 12:37:45 +0100

> I was wondwering about the 'lambda' construction in Python. It seems to
> me thats its semantics are backwards. Say I do something like this,
>
> n = 1
> f = lambda x: x + n
>
> I would expect f to be the function f(x)=x+1. This is how a standard
> term closure works in a functional language.

Why not the function f(x)=x+n? That's how closures work in Scheme as
far as I know. (The difference is whether a later assignment to n
affects the meaning of f -- in Scheme it does).

> To get the same effect in Python I must do,
>
> f = eval("lambda x: x + n", {n:n})

That's gross. As a general rule, when you're applying eval() to a
literal, there's generally a better way. (I presume you meant {'n':n}
since as written it doesn't work.)

I'll show how to do it in a more Pythonic way, but first a little
digression on scopes in Python. Python has a problem with nesting
scopes -- it doesn't let you. This was a conscious, though not
uncontroversial design descision. There are, like in C, two
use-defined scope levels: global (the top level of each module) and
local (the current function). There's also the built-in scope --
containing built-in functions like len(), the exception identifiers
etc.), but it is read-only and its contents are [supposedly] static,
so it can be left out of this discussion.

A rationale for the decision to forbid access to intermediate scopes
was that its major use is to structure programs by nesting functions.
As was shown by Pascal, this is not the right way. Python has
sufficient other mechanisms for creating program structure (classes,
modules, perhaps soon packages) that support for nesting functions was
left out. This is also a major simplification in the implementation.

When lambda was added to the language, it didn't introduce any new
power -- it's just a shortcut for a small function. Therefore it also
doesn't have access to even the immediately surrounding scope. This
is no problem if you want to use it for simple cases like

filter(lambda x: x > 10, some_table)

to find the elements > 10 in a table, or

list.sort(lambda x, y: cmp(x.name, y.name))

to sort a hypothetical list of employee records by name.

Now, back to the big question: how to define f(x)=x+n, given a
variable n? The simple solution is to use default arguments. This
works (giving the first semantics, actually):

f = lambda x, n=n: x+n

Although it's idiomatic to use "n=n", it's perhaps clearer to write it
like this:

f = lamda x, y=n: x+y

The reason this works is that the expressions for default argument
values are evaluated in the defining scope, at the time the function
is being defined. This,

n = 1
f = lambda x, y=n: x+y

is equivalent to

f = lambda x, y=1: x+y

which gives f(x)=x+1.

> As I think most programmers use lambda constructions as term closures,
> I would like to see the default behaviour for lambda constructions changed
> to form closures.

Two remarks here (yes I'm sligtly pissed off since this issue comes
back time after time): (1) pulling in the rest of the world to
strengthen your argument is a sign of weakness; (2) don't phrase your
own frustrations or misunderstandings as language change requests.
Sorry. That feels much better. Now on to more constructive things
:-)

> I came across this issue in a recent article I posted to the group. In it
> I asked,
>
> >I have a binding like this,
> >
> > self.tag_bind("some_name", "<ButtonRelease-2>", self.do_insert)
> >
> >I then have my do_insert function as follows,
> >
> > def do_insert(self, event):
> > ...
> >
> >Is there any way in do_insert of retrieving the string
> >"<ButtonRelease-2>"? Alternately is there any way of establishing
> >the binding such that this information is easy to access?
>
> The solution to this problem is to use a lambda construction. Say I had
> a set of possible event masks (a set of "<ButtonRelease-2>" specifications).
> Then I can write this code,
>
> for x in self.event_masks:
> self.tag_bind("_an_action", x,
> eval("lambda e: e.widget.do_action(e, x)", {'x':x}))

This is a good point to explain why using

lambda e, x=x: e.widget.do_action(e, x)

is much better than using eval(): since you're doing it in a loop, the
eval() form *parses* the lambda expression each time afresh, while if
you use the lambda expression directly it's already been passed and
only the initial value has to be reevaluated. This saves space as
well: each time the form is parsed, a new "code object" is generated,
while without eval(), all distinct lambda objects created share the
same code object.

Finally, there's one situation where using default arguments doesn't
work: when you want the lambda (or other function object you need to
create pass some values into) needs to be called with an unbounded
variable number of parameters. In this case, a solution is to use
classes. If there's interest I'll show how in a next installment...

--Guido van Rossum, CWI, Amsterdam <mailto:Guido.van.Rossum@cwi.nl>
<http://www.cwi.nl/cwi/people/Guido.van.Rossum.html>