Lambda forms and scoping

R. David Murray rdmurray at bitdance.com
Sun Mar 22 15:42:21 EDT 2009


"Gabriel Genellina" <gagsl-py2 at yahoo.com.ar> wrote:
> En Fri, 20 Mar 2009 23:16:00 -0300, alex goretoy
> <aleksandr.goretoy at gmail.com> escribió:
> 
> > i looks at lambdas as unbound functions(or super function), in the case
> > above we create the functions in a list places it in memory unboud, once
> > binding a call to the memory address space it returns the value
> >
> > it is basically same as doing this:
> > def f():
> >     print "f"
> >
> > a=f #unbound function, same as rename function
> > a() #bind call to address space
> 
> Mmm, I don't quite understand what you said. lambda creates functions that
> aren't different than functions created by def: apart from the name,
> they're really the same thing.

Oh, good, I'm not the only one for whom the above didn't make sense :)
I feel a little less dense now.

> And if you imply that *where* you call a function does matter, it does
> not. A function carries its own local namespace, its own closure, and its
> global namespace. At call time, no additional "binding" is done (except
> parameters -> arguments).
> 
> (and the address space is always the one of the running process)

I poked around in the API docs and experimented with func_closure and
related attributes, and after bending my brain for a while I think I
understand this.  The actual implementation of the closure is a single
list of 'cell' objects which represent namespace slots in the nested
scopes in which the closed-over function is defined.

But the fact that it is a single list is an implementation detail, and
the implementation is in fact carefully designed so that conceptually
we can think of the closure as giving the function access to those
nested-scope namespaces in almost(*) the same sense that it has a
reference to the global and local namespaces.  That is, if what a name
in _any_ of those namespaces points to is changed, then the closed-over
function sees those changes.

In this way, we understand the original example:  when defining a
lambda having a 'free variable' (that is, one not defined in either the
local or global scope) that was a name in the surrounding function's
local namespace, the lambda is going to see any changes made by the
surrounding function with regards to what that name points to.  Thus,
the final value that the lambda uses is whatever the final value of the
for loop variable was when the surrounding function finished executing.

However, I think that a Python closure is not quite the same thing as a
'computer science' closure, for the same reason that people coming from a
language with variables-and-values as opposed to namespaces get confused
when dealing with Python function call semantics.  Consider:

    http://en.wikipedia.org/wiki/Closure_(computer_science)

That says that a closure can be used to provide a function with a private
set of variables that persist from one invocation to the next, so that
a value established in one call can be accessed in the next.  The last
part of that sentence is not true in Python, since any assignment inside
a function affects only the local (per-invocation) namespace or (given
a global statement) the global namespace.  A function cannot change the
thing pointed to by a name in the closure.  Only the outer function,
for whom that name is in its local namespace, can do that.

(*) That last sentence in the previous paragraph is why I said '_almost_
the same sense' earlier: a function can modify what names point to in
its local and global namespaces, but cannot modify what names point to
in the closure namespace.

Of course, we can produce the same _effect_ as a computer science closure
in Python by using mutable objects...which is exactly parallel to the
difference between passing mutable or immutable objects in a function
call.

--
R. David Murray           http://www.bitdance.com




More information about the Python-list mailing list