3 ms·
Multi-line lambdas are fine: Python will accept newlines in certain parts of an expression, and you can use '\' for others; e.g. f = lambda x: [ x + y
by chriswarbo 3y ago
Multi-line lambdas are fine: Python will accept newlines in certain parts of an expression, and you can use '\' for others; e.g.
f = lambda x: [
x + y
for y in range(x)
if y % 2 == 0
]
>>> f(5)
[5, 7, 9]
Lambdas which perform multiple sequential steps are fine, since we can use tuples to evaluate expressions in order; e.g.
from sys import stdout
g = lambda x: (
stdout.write("Given {0}\n".format(repr(x))),
x.append(42),
stdout.write("Mutated to {0}\n".format(repr(x))),
len(x)
)[-1]
>>> my_list = [1, 2, 3]
>>> new_len = g(my_list)
Given [1, 2, 3]
Mutated to [1, 2, 3, 42]
>>> new_len
4
>>> my_list
[1, 2, 3, 42]
The problem is that many things in Python require statements, and lambdas cannot contain any; not even one. For example, all of the following are single lines:
>>> throw = lambda e: raise e
File "<stdin>", line 1
throw = lambda e: raise e
^^^^^
SyntaxError: invalid syntax
>>> identity = lambda x: return x
File "<stdin>", line 1
identity = lambda x: return x
^^^^^^
SyntaxError: invalid syntax
>>> abs = lambda n: -1 * (n if n < 0 else return n)
File "<stdin>", line 1
abs = lambda n: -1 * (n if n < 0 else return n)
^^^^^^
SyntaxError: invalid syntax
>>> repeat = lambda f, n: for _ in range(n): f()
File "<stdin>", line 1
repeat = lambda f, n: for _ in range(n): f()
^^^
SyntaxError: invalid syntax
>>> set_key = lambda d, k, v: d[k] = v
File "<stdin>", line 1
set_key = lambda d, k, v: d[k] = v
^^^^^^^^^^^^^^^^^^^^
SyntaxError: cannot assign to lambda
>>> set_key = lambda d, k, v: (d[k] = v)
File "<stdin>", line 1
set_key = lambda d, k, v: (d[k] = v)
^^^^
SyntaxError: cannot assign to subscript here. Maybe you meant '==' instead of '='?
- mixmastamyk 3y agoNeat. I tried it with print(), works fine. Don't need return in a lambda. Assignment now has the walrus. Leaves raise and few other odds and ends. Do believe you've cracked it! Don't think I'll use it much, but you never know... in a pinch.
- chriswarbo 3y agoAh yes, I've been doing this with stdout.write since Python 2; it didn't occur to me that when Python 3 turned print into a function that would make it usable in lambdas! You're right that the walrus makes assignment more usable; we can also call methods like .__setitem__ to get similar effects. Unfortunately the walrus seems to suffer the same broken/ambiguous scoping as assignment statements, e.g. >>> a = 1 >>> b = lambda: (print(a), a := 2) >>> b() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <lambda> UnboundLocalError: cannot access local variable 'a' where it is not associated with a value > Don't need return in a lambda `return` is needed for early returns. Notice that my `abs` example is trying to do that with `return n` (to skip the `-1 *` operation, since `n` is non-negative in that case). > Leaves raise and a few other odds and ends Off the top of my head: yield, match, def, import, class, with, for, while, assert, try, except, async, await.
- mixmastamyk 3y agob = lambda: (a := 2, print(a)) Hmm, need to put the assignment before. Can't access nonlocals unless you only read the the var and don't write to it. That's the way functions in python work as well.
- chriswarbo 3y ago> Hmm, need to put the assignment before. No, that would not have the intended behaviour: your `print(a)` is reading a fresh local variable, defined by the `:=` expression (which shadows that from the outer scope), so it will output '2'. The intended behaviour is to print '1' and reassign the outer name; but we can't do that (that's why I chose it as an example!) > Can't access nonlocals unless you only read the the var and don't write to it. That's the way functions in python work as well. Yes, that is precisely what I meant when I said it "suffers the same broken/ambiguous scoping as assignment statements". Python took a haphazard, WorseIsBetter approach to assignment/declaration/scoping. The resulting behaviour is complex, confusing and involves spooky action-at-a-distance (e.g. the meaning of my `print(a)` expression was altered by the presence of an ':=' expression which hadn't been reached yet!). Since this doesn't really affect simple "top to bottom" scripts, it only became an issue once large applications and frameworks started emerging; by that time it was too late to fix, since there was too much Python code in the wild to justify such a large breaking change :(