Why I hate lambdas (Re: Do any of you recommend Python as a first programming language?)

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sun Mar 23 21:21:32 EDT 2008


On Sun, 23 Mar 2008 13:51:34 -0400, Roy Smith wrote:

> On the other hand, when I do:
> 
> def torture():
>     woman.putInChair()
>     cushion.poke()
>     rack.turn()
> 
> I've also done two things.  First, I've created a function object (i.e.
> a lambda body), and I've also bound the name torture to that function
> object, in much the same way I did with the list.  But, it's different. 
> The function object KNOWS that it's name is torture.

No it does not. Function objects don't know their name. All they know is 
that they have a label attached to them that is useful to use as a name 
in some contexts, e.g. when printing tracebacks. It's just a label, 
nothing more.

You can prove that for yourself with a few simple tests. Firstly, we can 
prove that functions don't know what they are called by writing a 
recursive function:


def spam(n):
    if n <= 1: return "Spam"
    return "Spam " + spam(n-1)


If spam() knows what it is called, then you should be able to rename the 
function and the recursive call will continue to work. But in fact, 
that's not what happens:


>>> spam(3)  # check that it works
'Spam Spam Spam'
>>> tasty_stuff = spam  # make an alias
>>> tasty_stuff(3)
'Spam Spam Spam'


But now watch what happens when we create a new function with the name 
spam. It hijacks the recursive call:


>>> def spam(n):
...     return "ham-like meat product"
...
>>> tasty_stuff(3)
'Spam ham-like meat product'

The function formerly known as "spam" isn't calling itself, it is merely 
calling a function by name "spam". But we can see that the function 
tasty_stuff() is still using the old "spam" label for itself:

>>> tasty_stuff('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in spam
TypeError: unsupported operand type(s) for -: 'str' and 'int'


Unfortunately there's nothing we can do to fix that error. Even though 
the function object has an attribute "__name__" (also known as 
"func_name") which is set to spam, it isn't used for tracebacks. Instead, 
the label comes from a read-only attribute buried deep in the function 
object:

>>> tasty_stuff.func_code.co_name = 'yummy meat-like product in a can'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute


This is a mistake, in my opinion. It's an arbitrary decision to make this 
read-only (as far as I can tell), which goes against the grain of 
Python's "we're all consenting adults here" philosophy.

By the way, in case you're thinking that wanting to change the (so-
called) name of a function is a silly think to do, not at all. Consider 
factory functions:

def factory(how_much):
    def f(n=1):
        for i in range(n):
            print "I love spam a %s" % how_much
    return f

Every function created by the factory has the same "name", no matter what 
name you actually use to refer to it. factory('little') and 
factory('lot') both uselessly identify themselves as "f" in tracebacks.

The truth is that objects don't know what name they have, because objects 
don't have names. The relationship is the other way around: names have 
objects, not vice versa. Some objects (functions, classes, anything 
else?) usefully need a label so that they can refer to themselves in 
tracebacks and similar, and we call that label "the name", but it's just 
a label. It doesn't mean anything.


[snip]

> What Python give us with lambdas is some half-way thing.  It's not a
> full function, so it's something that people use rarely, 

Which people?

> which means most people (like me) can't remember the exact syntax. 

Speak for yourself, not for "most people".


> Even when I know
> it's the right thing to be using in a situation, I tend not to use it
> simply because the path of least resistance is to write a one-off
> function vs. looking up the exact syntax for a lambda in the manual.

lambda arguments : expression

Why is that harder to remember than this?

def name ( arguments ) :
    block  

And don't forget to include a return statement, or you'll be surprised by 
the result of the function.



-- 
Steven



More information about the Python-list mailing list