[Python-ideas] Multi Statement Lambdas

Steven D'Aprano steve at pearwood.info
Sun Oct 21 18:46:22 EDT 2018


On Sun, Oct 21, 2018 at 11:04:02PM +0200, Vladimir Filipović wrote:

> Sometimes I need that callable to be something very simple, but it
> technically can't be a lambda because it has a statement in it.
> Defining it as a function forces me to give it not only a name, but a
> place too. Defining it just before the point of use is a natural
> location in one sense, but it makes the visual flow of the code worse,
> especially if I need to make several such steps (with different
> almost-lambdas) in a row.
> 
> 
> From the standard library, I can actually think of Thread:
> 
> t1 = Thread(target=(lambda: counter += 1))
> t2 = Thread(target=(lambda: sum += current_quux()))
> 
> Because they include statements, it needs to be:
> 
> def inc_counter():
>     counter += 1
> t1 = Thread(target=inc_counter)


I don't think that's a real working example.

py> counter = 0
py> def f():
...     counter += 1
...
py> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'counter' referenced before assignment

You need to declare counter and sum as global variables.


> def update_sum():
>     sum += current_quux()
> t2 = Thread(target=update_sum)
> 
> 
> From one popular library:
> 
> ws = websocket.WebSocketApp(
>     url,
>     on_open = (lambda ws: ws.send('subscribe'); conn_counter += 1),
>     on_message = (lambda ws, msg: print(msg); msg_counter += 1))
> 
> Because they include statements, I again need to create those
> functions separately. Naming isn't so much a problem here, but it
> would turn kludgey if I needed to make several such calls in the same
> scope.

Its not the naming that would make it turn kludgy, but the use of global 
variables. If you had three websockets, would you want them all to share 
the same counter?


> In any case, that's a common little frustration I often put up with. I
> think most other languages around Python's power level don't make that
> a problem.
> 
> 
> To expand that argument a bit, lambdas have access to names in their
> immediate context that can be more of a pain with less-local
> functions. For a toy example:
> 
> [(lambda a: print(x + a)) for x in l]

You've fallen straight into the classic eager versus late binding of 
closures Gotcha.


py> l = "spam eggs cheese aardvark".split()
py> funcs = [(lambda a: print(x + a)) for x in l]
py> for f in funcs:
...     f("")
...
aardvark
aardvark
aardvark
aardvark


If your intention was to demonstrate that multi-statement lambda would 
be a bug-magnet, you have done an excellent job :-)



-- 
Steve


More information about the Python-ideas mailing list