Weird lambda rebinding/reassignment without me doing it

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sat Jul 12 11:40:33 EDT 2008


On Thu, 10 Jul 2008 14:09:16 -0400, Terry Reedy wrote:

>>>>>> g = lambda x:validate(x)
> 
> This is doubly diseased.
> 
> First, never write a 'name = lambda...' statement since it is equivalent
> to a def statement except that the resulting function object lacks a
> proper .funcname attribute.

Using lambda in this way is no more "diseased" than aliasing any other 
object. It's a matter of personal preference not to bind a lambda to a 
name. Functions, whether created by lambda or def, are first class 
objects, and as such there's nothing wrong with binding them to names.

Admittedly, the lack of a func_name attribute can sometimes make 
tracebacks harder to understand, especially if you've got many bound 
lambdas. But that's no worse than this:

>>> def factory(n):
...     def f():
...             return "spam"*n
...     return f
...
>>> one_spam = factory(1)
>>> two_spam = factory(2)
>>> one_spam.func_name == two_spam.func_name
True

I'm sure that nobody would argue that using factory functions is 
"diseased" because the functions have the same func_name attribute -- 
even if it does sometimes make it harder to interpret tracebacks:

>>> one_spam()
'spam'
>>> one_spam('arg')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes no arguments (1 given)


So by all means say that you personally don't like to bind lambdas to 
names, but don't pretend that it is somehow wrong to do so.



> Second, never write a function (with either def or lambda) that simply
> returns a function of the argument(s).

I think you need to reconsider the way you say that. By definition, 
*every* function simply returns a function of the arguments. That's what 
functions do.

> The wrapping does nothing!  This
> is a waste of time and space for both you and the interpreter.

When I was a newbie, I used to write:

def sin(x):
    return math.sin(x)

That was before I learned to just do this:

sin = math.sin

But just to prove that you should never say never:

>>> import timeit
>>> T1 = timeit.Timer("f(1)", """def g(x):
...     return x+1
...
... f = g
... """)
>>> T2 = timeit.Timer("f(1)", """def g(x):
...     return x+1
...
... def f(x):
...     return g(x)
... """)
>>>
>>> T1.repeat()
[0.50848197937011719, 0.54573416709899902, 0.54767107963562012]
>>> T2.repeat()
[0.88546991348266602, 0.89811587333679199, 0.95785093307495117]

So that's a good reason to wrap a function in another function -- to 
measure the overhead of a function call.

*wink*


-- 
Steven



More information about the Python-list mailing list