[python] No Multiline Lambda in Python: Why not?

I've heard it said that multiline lambdas can't be added in Python because they would clash syntactically with the other syntax constructs in Python. I was thinking about this on the bus today and realized I couldn't think of a single Python construct that multiline lambdas clash with. Given that I know the language pretty well, this surprised me.

Now, I'm sure Guido had a reason for not including multiline lambdas in the language, but out of curiosity: what's a situation where including a multiline lambda would be ambiguous? Is what I've heard true, or is there some other reason that Python doesn't allow multiline lambdas?

This question is related to python syntax lambda

The answer is


Here's a more interesting implementation of multi line lambdas. It's not possible to achieve because of how python use indents as a way to structure code.

But luckily for us, indent formatting can be disabled using arrays and parenthesis.

As some already pointed out, you can write your code as such:

lambda args: (expr1, expr2,... exprN)

In theory if you're guaranteed to have evaluation from left to right it would work but you still lose values being passed from one expression to an other.

One way to achieve that which is a bit more verbose is to have

lambda args: [lambda1, lambda2, ..., lambdaN]

Where each lambda receives arguments from the previous one.

def let(*funcs):
    def wrap(args):
        result = args                                                                                                                                                                                                                         
        for func in funcs:
            if not isinstance(result, tuple):
                result = (result,)
            result = func(*result)
        return result
    return wrap

This method let you write something that is a bit lisp/scheme like.

So you can write things like this:

let(lambda x, y: x+y)((1, 2))

A more complex method could be use to compute the hypotenuse

lst = [(1,2), (2,3)]
result = map(let(
  lambda x, y: (x**2, y**2),
  lambda x, y: (x + y) ** (1/2)
), lst)

This will return a list of scalar numbers so it can be used to reduce multiple values to one.

Having that many lambda is certainly not going to be very efficient but if you're constrained it can be a good way to get something done quickly then rewrite it as an actual function later.


[Edit] Read this answer. It explains why multiline lambda is not a thing.

Simply put, it's unpythonic. From Guido van Rossum's blog post:

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.


On the subject of ugly hacks, you can always use a combination of exec and a regular function to define a multiline function like this:

f = exec('''
def mlambda(x, y):
    d = y - x
    return d * d
''', globals()) or mlambda

You can wrap this into a function like:

def mlambda(signature, *lines):
    exec_vars = {}
    exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
    return exec_vars['mlambda']

f = mlambda('(x, y)',
            'd = y - x',
            'return d * d')

I was just playing a bit to try to make a dict comprehension with reduce, and come up with this one liner hack:

In [1]: from functools import reduce
In [2]: reduce(lambda d, i: (i[0] < 7 and d.__setitem__(*i[::-1]), d)[-1], [{}, *{1:2, 3:4, 5:6, 7:8}.items()])                                                                                                                                                                 
Out[3]: {2: 1, 4: 3, 6: 5}

I was just trying to do the same as what was done in this Javascript dict comprehension: https://stackoverflow.com/a/11068265


Let me present to you a glorious but terrifying hack:

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  gives: 2
  '''
  if len(bindings) == 0:
    return body(env)

  env = env or _obj()
  k, v = bindings[:2]
  if isinstance(v, types.FunctionType):
    v = v(env)

  setattr(env, k, v)
  return LET(bindings[2:], body, env)

You can now use this LET form as such:

map(lambda x: LET(('y', x + 1,
                   'z', x - 1),
                  lambda o: o.y * o.z),
    [1, 2, 3])

which gives: [0, 3, 8]


Let me try to tackle @balpha parsing problem. I would use parentheses around the multiline lamda. If there is no parentheses, the lambda definition is greedy. So the lambda in

map(lambda x:
      y = x+1
      z = x-1
      y*z,
    [1,2,3]))

returns a function that returns (y*z, [1,2,3])

But

map((lambda x:
      y = x+1
      z = x-1
      y*z)
    ,[1,2,3]))

means

map(func, [1,2,3])

where func is the multiline lambda that return y*z. Does that work?


This is generally very ugly (but sometimes the alternatives are even more ugly), so a workaround is to make a braces expression:

lambda: (
    doFoo('abc'),
    doBar(123),
    doBaz())

It won't accept any assignments though, so you'll have to prepare data beforehand. The place I found this useful is the PySide wrapper, where you sometimes have short callbacks. Writing additional member functions would be even more ugly. Normally you won't need this.

Example:

pushButtonShowDialog.clicked.connect(
    lambda: (
    field1.clear(),
    spinBox1.setValue(0),
    diag.show())

You can simply use slash (\) if you have multiple lines for your lambda function

Example:

mx = lambda x, y: x if x > y \
     else y
print(mx(30, 20))

Output: 30

Let me also throw in my two cents about different workarounds.

How is a simple one-line lambda different from a normal function? I can think only of lack of assignments, some loop-like constructs (for, while), try-except clauses... And that's it? We even have a ternary operator - cool! So, let's try to deal with each of these problems.

Assignments

Some guys here have rightly noted that we should take a look at lisp's let form, which allows local bindings. Actually, all the non state-changing assignments can be performed only with let. But every lisp programmer knows that let form is absolutely equivalent to call to a lambda function! This means that

(let ([x_ x] [y_ y])
  (do-sth-with-x-&-y x_ y_))

is the same as

((lambda (x_ y_)
   (do-sth-with-x-&-y x_ y_)) x y)

So lambdas are more than enough! Whenever we want to make a new assignment we just add another lambda and call it. Consider this example:

def f(x):
    y = f1(x)
    z = f2(x, y)
    return y,z

A lambda version looks like:

f = lambda x: (lambda y: (y, f2(x,y)))(f1(x))

You can even make the let function, if you don't like the data being written after actions on the data. And you can even curry it (just for the sake of more parentheses :) )

let = curry(lambda args, f: f(*args))
f_lmb = lambda x: let((f1(x),), lambda y: (y, f2(x,y)))
# or:
f_lmb = lambda x: let((f1(x),))(lambda y: (y, f2(x,y)))

# even better alternative:
let = lambda *args: lambda f: f(*args)
f_lmb = lambda x: let(f1(x))(lambda y: (y, f2(x,y)))

So far so good. But what if we have to make reassignments, i.e. change state? Well, I think we can live absolutely happily without changing state as long as task in question doesn't concern loops.

Loops

While there's no direct lambda alternative for loops, I believe we can write quite generic function to fit our needs. Take a look at this fibonacci function:

def fib(n):
    k = 0
    fib_k, fib_k_plus_1 = 0, 1
    while k < n:
        k += 1
        fib_k_plus_1, fib_k = fib_k_plus_1 + fib_k, fib_k_plus_1
    return fib_k

Impossible in terms of lambdas, obviously. But after writing a little yet useful function we're done with that and similar cases:

def loop(first_state, condition, state_changer):
    state = first_state
    while condition(*state):
        state = state_changer(*state)
    return state

fib_lmb = lambda n:\
            loop(
              (0,0,1),
              lambda k, fib_k, fib_k_plus_1:\
                k < n,
              lambda k, fib_k, fib_k_plus_1:\
                (k+1, fib_k_plus_1, fib_k_plus_1 + fib_k))[1]

And of course, one should always consider using map, reduce and other higher-order functions if possible.

Try-except and other control structs

It seems like a general approach to this kind of problems is to make use of lazy evaluation, replacing code blocks with lambdas accepting no arguments:

def f(x):
    try:    return len(x)
    except: return 0
# the same as:
def try_except_f(try_clause, except_clause):
    try: return try_clause()
    except: return except_clause()
f = lambda x: try_except_f(lambda: len(x), lambda: 0)
# f(-1) -> 0
# f([1,2,3]) -> 3

Of course, this is not a full alternative to try-except clause, but you can always make it more generic. Btw, with that approach you can even make if behave like function!

Summing up: it's only natural that everything mentioned feels kinda unnatural and not-so-pythonically-beautiful. Nonetheless - it works! And without any evals and other trics, so all the intellisense will work. I'm also not claiming that you shoud use this everywhere. Most often you'd better define an ordinary function. I only showed that nothing is impossible.


Guido van Rossum (the inventor of Python) answers this exact question himself in an old blog post.
Basically, he admits that it's theoretically possible, but that any proposed solution would be un-Pythonic:

"But the complexity of any proposed solution for this puzzle is immense, to me: it requires the parser (or more precisely, the lexer) to be able to switch back and forth between indent-sensitive and indent-insensitive modes, keeping a stack of previous modes and indentation level. Technically that can all be solved (there's already a stack of indentation levels that could be generalized). But none of that takes away my gut feeling that it is all an elaborate Rube Goldberg contraption."


A couple of relevant links:

For a while, I was following the development of Reia, which was initially going to have Python's indentation based syntax with Ruby blocks too, all on top of Erlang. But, the designer wound up giving up on indentation sensitivity, and this post he wrote about that decision includes a discussion about problems he ran into with indentation + multi-line blocks, and an increased appreciation he gained for Guido's design issues/decisions:

http://www.unlimitednovelty.com/2009/03/indentation-sensitivity-post-mortem.html

Also, here's an interesting proposal for Ruby-style blocks in Python I ran across where Guido posts a response w/o actually shooting it down (not sure whether there has been any subsequent shoot down, though):

http://tav.espians.com/ruby-style-blocks-in-python.html


In Python 3.8/3.9 there is Assignment Expression, so it could be used in lambda, greatly expanding functionality

E.g., code

#%%
x = 1
y = 2

q = list(map(lambda t: (tx := t*x, ty := t*y, tx+ty)[-1], [1, 2, 3]))

print(q)

will print [3, 6, 9]


because a lambda function is supposed to be one-lined, as its the simplest form of a function, an entrance, then return


I am starting with python but coming from Javascript the most obvious way is extract the expression as a function....

Contrived example, multiply expression (x*2) is extracted as function and therefore I can use multiline:

def multiply(x):
  print('I am other line')
  return x*2

r = map(lambda x : multiply(x), [1, 2, 3, 4])
print(list(r))

https://repl.it/@datracka/python-lambda-function

Maybe it does not answer exactly the question if that was how to do multiline in the lambda expression itself, but in case somebody gets this thread looking how to debug the expression (like me) I think it will help


I know it is an old question, but for the record here is a kind of a solution to the problem of multiline lambda problem in which the result of one call is consumed by another call.

I hope it is not super hacky, since it is based only on standard library functions and uses no dunder methods.

Below is a simple example in which we start with x = 3 and then in the first line we add 1 and then in the second line we add 2 and get 6 as the output.

from functools import reduce

reduce(lambda data, func: func(data), [
    lambda x: x + 1,
    lambda x: x + 2
], 3)

## Output: 6

I'm guilty of practicing this dirty hack in some of my projects which is bit simpler:

    lambda args...:( expr1, expr2, expr3, ...,
            exprN, returnExpr)[-1]

I hope you can find a way to stay pythonic but if you have to do it this less painful than using exec and manipulating globals.


(For anyone still interested in the topic.)

Consider this (includes even usage of statements' return values in further statements within the "multiline" lambda, although it's ugly to the point of vomiting ;-)

>>> def foo(arg):
...     result = arg * 2;
...     print "foo(" + str(arg) + ") called: " + str(result);
...     return result;
...
>>> f = lambda a, b, state=[]: [
...     state.append(foo(a)),
...     state.append(foo(b)),
...     state.append(foo(state[0] + state[1])),
...     state[-1]
... ][-1];
>>> f(1, 2);
foo(1) called: 2
foo(2) called: 4
foo(6) called: 12
12

Examples related to python

programming a servo thru a barometer Is there a way to view two blocks of code from the same file simultaneously in Sublime Text? python variable NameError Why my regexp for hyphenated words doesn't work? Comparing a variable with a string python not working when redirecting from bash script is it possible to add colors to python output? Get Public URL for File - Google Cloud Storage - App Engine (Python) Real time face detection OpenCV, Python xlrd.biffh.XLRDError: Excel xlsx file; not supported Could not load dynamic library 'cudart64_101.dll' on tensorflow CPU-only installation

Examples related to syntax

What is the 'open' keyword in Swift? Check if returned value is not null and if so assign it, in one line, with one method call Inline for loop What does %>% function mean in R? R - " missing value where TRUE/FALSE needed " Printing variables in Python 3.4 How to replace multiple patterns at once with sed? What's the meaning of "=>" (an arrow formed from equals & greater than) in JavaScript? How can I fix MySQL error #1064? What do >> and << mean in Python?

Examples related to lambda

Java 8 optional: ifPresent return object orElseThrow exception How to properly apply a lambda function into a pandas data frame column What are functional interfaces used for in Java 8? Java 8 lambda get and remove element from list Variable used in lambda expression should be final or effectively final Filter values only if not null using lambda in Java8 forEach loop Java 8 for Map entry set Java 8 Lambda Stream forEach with multiple statements Java 8 stream map to list of keys sorted by values Task.Run with Parameter(s)?