Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Can someone explain the concrete reasons why BDFL choose to make Python lambdas single line?

This is good:

lambda x: x**x

This results in an error:

lambda x:
    x**x

I understand that making lambda multi-line would somehow "disturb" the normal indentation rules and would require adding more exceptions, but isn't that worth the benefits?

Look at JavaScript, for example. How can one live without those anonymous functions? They're indispensable. Don't Pythonistas want to get rid of having to name every multi-line function just to pass it as an argument?

share|improve this question
2  
Considering that you note the concrete reasons why Guido doesn't allow multi-expression lambdas and then dismiss them, I'm going to assume you're seeking validation rather than a real answer. –  Jason Baker Aug 7 '11 at 18:13
2  
Apart from saving seven characters, how is this any better than a def? It now has exactly the same visual structure. –  detly Aug 8 '11 at 1:28
    

4 Answers 4

up vote 18 down vote accepted

Guido van van Rossum answered it himself:

But such solutions often lack "Pythonicity" -- that elusive trait of a good Python feature. It's impossible to express Pythonicity as a hard constraint. Even the Zen of Python doesn't translate into a simple test of Pythonicity...

In the example above, it's easy to find the Achilles heel of the proposed solution: the double colon, while indeed syntactically unambiguous (one of the "puzzle constraints"), is completely arbitrary and doesn't resemble anything else in Python...

But I'm rejecting that too, because in the end (and this is where I admit to unintentionally misleading the submitter) I find any solution unacceptable that embeds an indentation-based block in the middle of an expression. Since I find alternative syntax for statement grouping (e.g. braces or begin/end keywords) equally unacceptable, this pretty much makes a multi-line lambda an unsolvable puzzle.

http://www.artima.com/weblogs/viewpost.jsp?thread=147358

Basically, he says that although a solution is possible, it's not congruent with how Python is.

share|improve this answer
1  
+1 thanks for the thread link -- but i would still appreciate multi-line lambdas -- they are invaluable -- look at JavaScript, PHP has them included also. –  good_computer Aug 7 '11 at 17:49
2  
@greengit You can just use a nested def. It isn't the same as anonymous functions, but they're close enough. –  jsternberg Aug 7 '11 at 17:58
    
nested defs don't help when passing functions as arguments -- that's the number one reason why i'd like multi-line lambds –  good_computer Aug 7 '11 at 18:03
1  
@greengit - I think you're better off taking this up with GvR than posting your comments here. –  Jason Baker Aug 7 '11 at 18:10
7  
@greengit: You do know that you can pass the function in as an argument to another function? You can't write it inline, but there is no programming technique that is not available to you. –  btilly Aug 7 '11 at 19:56

it's perfectly fine to do a multi line lambda in python: see

>>> f = lambda x: (
...   x**x)
>>> f
<function <lambda> at 0x7f95d8f85488>
>>> f(3)
27

the real lambda limitation is the fact that lambda must be a single expression; it can't contains keyword (like python2's print or return).

GvR choose to do so to limit the size of the lambda, as they normally are used as parameters. If you want a real function, use def

share|improve this answer
1  
multi line is about the insert of the '\n' character :D python does not have multi statement lambda. You really want to use def. Think about it: you really need a callable as a parameter of your function? And the users of that function aren't allowed to pass your default callable? How can they pass it if you don't give it to them? –  Vito De Tullio Aug 7 '11 at 18:14
    
btw, can you provide an example of your need of an anonimous function? –  Vito De Tullio Aug 7 '11 at 18:19

I know this is super old, but putting here as a reference.

An alternative to using lambda could be to use a def in a non-conventional way. The goal is to pass a def to a function, which can be done in only one circumstance -- a decorator. Notice that with this implementation def result does not create a function, it creates the result of reduce(), which ends up being a dict.

Shameless plug: I do a lot of this here.

>>> xs = [('a', 1), ('b', 2), ('a', 3), ('b', 4)]
>>> foldl = lambda xs, initial: lambda f: reduce(f, xs, initial)
>>> @foldl(xs, {})
... def result(acc, (k, v)):
...     acc.setdefault(k, 0)
...     acc[k] += v
...     return acc
...
>>> result
{'a': 4, 'b': 6} 

Note that multi-statement lambdas can be done, but only with really, really ugly code. However, what is interesting is how scoping works with this implementation (note the multiple usage of the name variable and the shadowing of the message variable.

>>> from __future__ import print_function
>>> bind = lambda x, f=(lambda x: x): f(x)
>>> main = lambda: bind(
...     print('Enter your name.'), lambda _: bind(
...     raw_input('> '), lambda name: bind(
...     'Hello {}!'.format(name), lambda message: bind(
...     print(message), lambda _: bind(
...     'Bye {}!'.format(name), lambda message: bind(
...     print(message)
... ))))))
>>> main()
Enter your name.
> foo
Hello foo!
Bye foo!
share|improve this answer
    
+1 for a monadic approach –  jozefg Nov 16 '13 at 13:54

Hacking together a multi-statement lambda isn't quite as bad as pyrospade makes out: sure we could compose a bunch of monadic functions using bind, like in Haskell, but since we're in the impure world of Python, we might as well use side-effects to achieve the same thing.

I cover a few ways to do this at http://chriswarbo.net/index.php?page=news&type=view&id=admin-s-blog%2Fanonymous-closures-in

For example, Python guarantees to evaluate the elements of a tuple in order, so we can use "," much like an imperative ";". We can replace many statements, like "print", with expressions, like "sys.stdout.write".

Hence the following are equivalent:

def print_in_tag_def(tag, text):
    print "<" + tag + ">"
    print text
    print "</" + tag + ">"

import sys
print_ = sys.stdout.write
print_in_tag_lambda = lambda tag, text: (print_("<" + tag + ">"),
                                         print_(text),
                                         print_("</" + tag + ">"),
                                         None)[-1]

Note that I've added a "None" at the end, and extracted it using "[-1]"; this sets the return value explicitly. We don't have to do this, but without it we'd get a funky "(None, None, None)" return value, which we may or may not care about.

So we can sequence IO actions. What about local variables?

Python's "=" forms an statement, so we need to find an equivalent expression. One way is to mutate the contents of datastructure, passed in as an argument. For example:

def stateful_def():
    foo = 10
    bar = foo * foo
    foo = 2
    return foo + bar

stateful_lambda = (lambda state: lambda *_: (state.setdefault('foo', 10),
                                             state.setdefault('bar', state.get('foo') * state.get('foo')),
                                             state.pop('foo'),
                                             state.setdefault('foo', 2),
                                             state.get('foo') + state.get('bar'))[-1])({})

Of course, this is cumbersome but we can make helper functions:

callWith = lambda x, f: f(x)
mkEnv = lambda _*: callWith({},
                            lambda d: (d.get,
                                       lambda k, v: (d.pop(k), d.setdefault(k, v))))
inEnv = lambda f: callWith(mkEnv(), f)

stateful_lambda = inEnv(lambda get, set: (set('foo', 10),
                                          set('bar', get('foo') * get('foo')),
                                          set('foo', 2),
                                          get('foo') + get('bar'))[-1])
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.