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
yand 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.
