0

Why does python3.8+ considers result as local variable in nested lambdas when using := but not otherwise?

>>> counter = (lambda result: (lambda: (result + 1)))(0)
>>> counter()
1
>>> counter = (lambda result: (lambda: (result := result + 1)))(0)
>>> counter()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
UnboundLocalError: local variable 'result' referenced before assignment
wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • 1
    A question title should be specific enough that someone looking at it in search results has a pretty good idea what the "this" you're asking about is. (I've tried to edit towards that end). – Charles Duffy Oct 25 '21 at 19:28
  • 3
    Same as with any non-lambda function. If you wrote this out with `def`, you'd get the same result. – user2357112 Oct 25 '21 at 19:29
  • BTW, welcome to Stack Overflow! Please take the [tour] and read [ask], which has tips on how to write a good title. – wjandrea Oct 25 '21 at 19:31
  • 2
    BTW, [named lambdas are bad practice](/q/38381556/4518341). Use a `def` instead. – wjandrea Oct 25 '21 at 19:33
  • Agreeing with @wjandrea here. If you're trying to use this code in production (and not just as an academic example), you should *really* rewrite this with [generators](https://wiki.python.org/moin/Generators). – Antimon Oct 25 '21 at 20:48
  • 1
    @Antimon Actually, I'd say don't reinvent the wheel, use [`itertools.count()`](https://docs.python.org/3/library/itertools.html#itertools.count) instead. (At least, I assume that's what OP's trying to do.) I assumed the code in the question is just an example. – wjandrea Oct 25 '21 at 20:51
  • Very good point! But in general, if you start writing nested lambdas, step away from the keyboard and ask yourself what you're doing. – Antimon Oct 25 '21 at 21:41

1 Answers1

5

The scoping rules for variables created by := are the same as they are for = (as long as a comprehension is not involved). result := result + 1 creates a new local variable named result that shadows the parameter of the outer function. As result is not defined before the assignment expression, trying to evaluate result + 1 produces a NameError.

As you cannot use a nonlocal statement in a lambda expression, there is no way to update the value of the outer function's result variable using a plain assignment. (While you could hack something together with the exec function, I recommend against it and will not show an example of how to do so here.)

chepner
  • 497,756
  • 71
  • 530
  • 681