This is a follow up on this post.
Default parameters are not the only way to do complex calculations in Python lambdas. Another idea is to use tuples and the “walrus” operator :=
available since Python 3.8. E.g., if we want to calculate (x2 + 2x + 7) + sqrt(x2 + 2x + 7), we can write a lambda as follows:
mymath = lambda x: ( y := x**2 + 2*x + 7, y + sqrt(y) )[-1]
Credit goes to 👤fleming for suggesting this brilliant option.
Let’s dissect what we are doing here:
- The lambda creates a tuple.
- The tuple has two items.
- The first item creates a variable named
y
and assigns a value. - The second item uses this variable.
- Finally, the lambda returns the last item for the tuple by applying the
[-1]
index.
Let’s consider a more interesting example:
delete_file = lambda file_path: ( os := __import__('os'), exists := os.path.exists(file_path), os.remove(file_path) if exists else None, exists )[-1] # create a test file test_file = "/tmp/testfile" with open(test_file, "w") as file: file.write(test_file) print(delete_file(test_file)) # prints True print(delete_file(test_file)) # prints False
This code imports os
module and deletes a file, all within a lambda.
With some ingenuity, we can do loops in a lambda and execute conditionals. Consider this implementation of the Fizzbuzz game:
fizzbuzz = lambda max_value: ( f15 := lambda n: "fizzbuzz" if n%15 == 0 else None, f5 := lambda n: "fizz" if n%3 == 0 else None, f3 := lambda n: "buzz" if n%5 == 0 else None, get_str := lambda n: f15(n) or f5(n) or f3(n) or str(n), loop := (print(get_str(n)) for n in range(1,max_value+1)), # deferred loop [x for x in loop if False] # execute the deferred loop, discarding all values )[-1] fizzbuzz(15) # prints 1, 2, fizz, 4, buzz, ...
The only thing you can’t really do in a lambda is to catch exceptions. This would work if we had a built-in function similar to this:
def do(f): try: return f(), None except Exception as e: return None, e
Alas, there is no such function. contextlib.suppress()
comes tantalizingly close, but it requires a with
statement, and we can’t have with
statements in a lambda either. Of course, we could define such function using exec()
, but then we could define any function using exec()
and the whole point of doing things in a lambda would be moot.
Why does it matter what we can run in a lambda? It has to do with safety of eval()
, which we’ll discuss next. Or at least at some point in the future. I have a draft post about it that has been lying around for a few months, and I intend to publish it soon.