[Python-Dev] PEP 572: Assignment Expressions
Steven D'Aprano
steve at pearwood.info
Sat Apr 21 08:26:44 EDT 2018
On Sat, Apr 21, 2018 at 05:46:44PM +1000, Chris Angelico wrote:
> On Sat, Apr 21, 2018 at 5:11 PM, Steven D'Aprano <steve at pearwood.info> wrote:
> > So can you explain specifically what odd function-scope behaviour you
> > are referring to? Give an example please?
>
> doubled_items = [x for x in (items := get_items()) if x * 2 in items]
>
> This will leak 'items' into the surrounding scope (but not 'x').
The "not x" part is odd, I agree, but it's a popular feature to have
comprehensions run in a separate scope, so that's working as designed.
The "leak items" part is the behaviour I desire, so that's not odd, it's
sensible *wink*
The reason I want items to "leak" into the surrounding scope is mostly
so that the initial value for it can be set with a simple assignment
outside the comprehension:
items = (1, 2, 3)
[ ... items := items*2 ... ]
and the least magical way to do that is to just make items an ordinary
local variable.
> [x for x in x if x] # This works
The oddity is that this does work, and there's no assignment expression
in sight.
Given that x is a local variable of the comprehension `for x in ...` it
ought to raise UnboundLocalError, as the expanded equivalent does:
def demo():
result = []
for x in x: # ought to raise UnboundLocalError
if x:
result.append(x)
return result
That the comprehension version runs (rather than raising) is surprising
but I wouldn't call it a bug. Nor would I say it was a language
guarantee that we have to emulate in similar expressions.
In the absence of either explicit documentation of this behaviour, or
Guido or one of the senior core developers explicitly stating that it is
intentional behaviour that should be considered a language promise, I'd
call it an accident of implementation.
In which case, the fact that your next example:
> [x for y in x if x := y] # UnboundLocalError
"correctly" raises, as does the expanded version:
def demo():
result = []
for y in x: # ought to raise UnboundLocalError
x = y # since x is a local
if x:
result.append(x)
return result
shouldn't be seen as a problem. The code is different, so why should it
behave the same?
> (x for x in 5) # TypeError
> (x for _ in [1] for x in 5) # Works
Now that last one is more than just odd, it is downright bizarre. Or at
least it would, if it did work:
py> list((x for _ in [1] for x in 5))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
TypeError: 'int' object is not iterable
Are you sure about this example?
In any case, since this has no assignment expression in it, I don't see
why it is relevant.
--
Steve
More information about the Python-Dev
mailing list