Something is rotten in Denmark...

Terry Reedy tjreedy at udel.edu
Tue May 31 19:14:09 EDT 2011


On 5/31/2011 4:18 PM, harrismh777 wrote:
> Terry Reedy wrote:
>> You have been hypnotizeed by lambda. (lambda n: i+n) is a *constant
>> expression*, so you get 10 'equal' functions.

> 'hypnotized' indeed!

I say 'hypnotized' ;-) because people have posted examples almost 
exactly like the one you did, with a list of 10 (usually) lambda-defined 
functions, at least yearly for over a decade. Never have I seen the same 
puzzlement when the functions are defined with def statements instead.


What you did differently is to continue investigating and discover some 
of the alternatives for yourself. Hence you get more effort from others 
in response.

I think part of the problem with lambda is this: the body of a def 
statement is indented and nicely set apart from surrounding code. The 
body of a lambda expression is prefixed by the header but there is no 
terminator to visually indicate the end of the nested scope. I think 
there should have been. I often add unneeded parens, as you did, but 
that is not lambda specific. Generator expressions have to be set apart 
with parentheses, and other comprehensions are all fenced.

There also seems to be a bit of lambda (oc)cultism and mystique of the 
anonymous. However, if a function raises an exception, anonymity is a 
defect.

What you did differently is to continue investigating and discover some 
of the alternatives for yourself.

 > ... ok, so let me see if I get this... the lambda
> defers lookup|bind of its references until such time as the lambda is
> 'called' and not at the time (as I thought) that the anonymous
> function(s) are returned?

In Python, there are no lambda objects to be called.

A def statement, when executed, produces an instance of the user-defined 
function class. Its .__name__ attribute is the one given in the header 
of the statement.
A lambda expression, when executed, produces an instance of the 
user-defined function class. Its .__name__ attribute is  '<lambda>' (at 
least in CPython).
See the single difference? The objects produced by equivalent def 
statements and lambda expressions are otherwise exactly the same.

statement with lambda arg-list: expression

is an abbreviation for

def new-name(arg-list): return expression
statement with new-name

assuming that the lambda in in the same scope as the statement and not 
nested in another scope-creating expression

> If I'm understanding that correctly, then that means lambda is working
> as designed, and that there are very subtle nuances to be aware of.

I would say that there is no subtle nuance. Except for not providing a 
name, a lambda expression is purely an optional syntactic abbreviation.


> In my little case
>
> (lambda n: i + n)
>
> ... if the i goes out of scope before the anonymous function gets called
> then we have a problem...

There was no problem; nonlocal names and their associations with objects 
get saved (in 'cells', for CPython) association with the function than 
needs them. One of my examples showed how to retrieve them.

 > or if i as a reference is mutable

Objects are mutable, names can be rebound.

 > or refers to a different object before the anonymous function
 > is called then we have a problem?

Anonymity is completely irrelevant to all of the above. Everything is 
the same as for def f(n): return i + n. When a function is compiled, 
each name is classified as local, non-local, or global. When the 
function is called and a name is evaluated, the corresponding object 
from its scope is retrieved, if there is one, or an error is raised.

> What I am discovering is that 'yes' I can use lambda syntactically where
> I might not be able to code a def statement;

If the lambda expression is nested in an abbreviated nested scope 
(lambda or comprehension), then you have to unabbreviate the outer 
nesting before you can unabbreviate the lambda. Several example of this 
have been shown.

> however, if I do use it (as in a list comprehension) then I may get
 > unexpected results if any of my
> lambda's references go out of scope or are mutable...(?)

Nothing needed goes out of scope and all names are rebindable (not
mutable).

> What are the ramifications of making the lookup|binding happen at the
> time the anonymous function is 'returned' vs 'called'?

This is early-binding versus late-binding. Python is a late-binding 
language.

Are you asking about changing all function compilation or only when 
functions are defined with lambda expressions? The latter *would* make 
there be a 'suble nuance' that Python now lacks and does not need.

> Has anyone suggested this?

Of course, more than once, in multiple variations. All have so far been 
rejected, more than once. The most sensible ideas are for earlier 
binding of builtins to make Python run faster.

You have already discovered and been given some of the solutions. 
Another is to define a class with a __call__ statement and save the 
desired early bindings as instance data attributes. Your g(i): return 
lambda ... as well as the default arg trick are functional equivalents 
of the same idea.

In general, if you want to immediately grab and save an object, then 
explicity grab and save the object.

-- 
Terry Jan Reedy




More information about the Python-list mailing list