(original) (raw)
\[Chris Barker\]
>>> So what about:>>>
>>> l = \[x:=i for i in range(3)\]
>>>
>>> vs
>>>
>>> g = (x:=i for i in range(3))
>>>
>>> Is there any way to keep these consistent if the "x" is in the regular local scope?
\[Tim\]
>> I'm not clear on what the question is. The list comprehension would
>> bind \` l \` to \[0, 1, 2\] and leave the local \`x\` bound to 2\. The second
>> example binds \`g\` to a generator object, which just sits there
>> unexecuted. That has nothing to do with the PEP, though.
>>
>> If you go on to do, e.g.,
>>
>> l = list(g)
>>
>> then, same as the listcomp, \`l\` will be bound to \[0, 1, 2\] and the local \`x\` will
>> example binds \`g\` to a generator object, which just sits there
>> unexecuted. That has nothing to do with the PEP, though.
>>
>> If you go on to do, e.g.,
>>
>> l = list(g)
>>
>> then, same as the listcomp, \`l\` will be bound to \[0, 1, 2\] and the local \`x\` will
>> be left bound to 2.
\[Chris\]
\[Chris\]
> OK, it has been said that the priority is that
>
> list(a\_gen\_expression)
>
> Behave the same as
>
> \[the\_same\_expression\]
That's certainly desirable.
>
> list(a\_gen\_expression)
>
> Behave the same as
>
> \[the\_same\_expression\]
That's certainly desirable.
> So we’re good there. And maybe it’s correct that leaving the running
> of the gen\_exp ‘till later is pretty uncommon, particularly for newbies,
Common or not, I have no idea why anyone would write a genexp like the one you gave, except to contrive an example of silly behavior exhibited by silly code ;-)
It's really not interesting to me to make up code as goofy as you can conceive of - the interesting questions are about plausible code (including plausible coding errors).
> but:
>
> If the execution of the gen\_exp is put off, it really confuses things
> — that name being changed would happen at some arbitrary tone, and at
> least in theory, the gen\_exp could be passed off to somewhere else in
> the code, and be run or not run completely remotely from where the
> name is used.
Sure.
> So while this is technically the same as the comprehension, it is not
> the same as a generator function which does get its own scope.
>
> If the execution of the gen\_exp is put off, it really confuses things
> — that name being changed would happen at some arbitrary tone, and at
> least in theory, the gen\_exp could be passed off to somewhere else in
> the code, and be run or not run completely remotely from where the
> name is used.
Sure.
> So while this is technically the same as the comprehension, it is not
> the same as a generator function which does get its own scope.
It is the same as a generator function with appropriate scope declarations - a generator expression is, after all, implemented \_by\_ a nested generator function. You can write a workalike to your code above today, but nobody worries about that because nobody does that ;-)
def f():
def bashx(outermost):
nonlocal x
for i in outermost:
x = i
yield i
x = 12
g = bashx(range(3))
print("x before", x)
L = list(g)
print("L", L)
print("x after", x)
Then calling \`f()\` prints:
L \[0, 1, 2\]
x after 2
> handling of the looping name was handled differently in gen\_exp vs
> comprehensions.
The PEP specifies the semantics. If it's accepted, that will be folded into the docs.
> So I think a local scope for all comprehension-like things would be
> the way to go.
>
> But getting back to the original thread topic — python has a number of
> places that you can only use expressions — adding the ability to bind
> a name in all these places complicates the language significantly.
> But getting back to the original thread topic — python has a number of
> places that you can only use expressions — adding the ability to bind
> a name in all these places complicates the language significantly.
Did adding ternary \`if\` (truepart if expression else falsepart) complicate the language significantly? Python has rarely expanded the number of expression forms, but whenever it has the sky didn't actually fall despite earnest warnings that disaster was inevitable ;-)
>> Put a body B in a listcomp and any side effects due to executing B
> Maybe it’s just me, but re-binding a name seems like a whole new
> category of side effect.
With no trickery at all, you've always been able to rebind attributes, and mutate containers, in comprehensions and genexps. Because \`for\` targets aren't limited to plain names; e.g.,
g = (x+y for object.attribute, a\[i\]\[j\] in zip(range(3), range(3)))
is already "legal", and will stomp all over the complex \`for\` targets when executed - there's nothing "local" about them. But nobody worries about that because nobody does stuff like that.
And as in my goofy code above, mucking with binding of plain names is also possible today. Indeed, straightforward if that's what you \_want\_ to do. But nobody does.
It's just not one of Python's goals to make it impossible to write useless code ;-)
is already "legal", and will stomp all over the complex \`for\` targets when executed - there's nothing "local" about them. But nobody worries about that because nobody does stuff like that.
And as in my goofy code above, mucking with binding of plain names is also possible today. Indeed, straightforward if that's what you \_want\_ to do. But nobody does.
It's just not one of Python's goals to make it impossible to write useless code ;-)