On Jun 27, 2018, at 9:49 AM, Steven D'Aprano firstname.lastname@example.org wrote:
On Wed, Jun 27, 2018 at 08:00:20AM -0400, Eric V. Smith wrote:
On 6/27/2018 7:08 AM, Chris Angelico wrote: It gets funnier with nested loops. Or scarier. I've lost the ability to distinguish those two.
def test(): spam = 1 ham = 2 vars = [key1+key2 for key1 in locals() for key2 in locals()] return vars
Wanna guess what that's gonna return?
I'm not singling out Chris here, but these discussions would be easier to follow and more illuminating if the answers to such puzzles were presented when they're posed.
You can just copy and paste the function into the interactive interpreter and run it :-)
Not on my phone when I’m riding a bus, I can’t. I’m trying to more or less follow the discussion, but the “guess what this will do” aspect of the discussion makes it hard.
But where's the fun in that? The point of the exercise is to learn first hand just how complicated it is to try to predict the *current* scope behaviour of comprehensions. Without the ability to perform assignment inside them, aside from the loop variable, we've managed to avoid thinking too much about this until now.
It also demonstrates the unrealisticness of treating comprehensions as a separate scope -- they're hybrid scope, with parts of the comprehension running in the surrounding local scope, and parts running in an sublocal scope.
Earlier in this thread, Nick tried to justify the idea that comprehensions run in their own scope, no matter how people think of them -- but that's an over-simplification, as Chris' example above shows. Parts of the comprehension do in fact behave exactly as the naive model would suggest (even if Nick is right that other parts don't).
As complicated and hairy as the above example is, (1) it is a pretty weird thing to do, so most of us will almost never need to consider it; and (2) backwards compatibility requires that we live with it now (at least unless we introduce a __future__ import).
If we can't simplify the scope of comprehensions, we can at least simplify the parts that actually matters. What matters are the loop variables (already guaranteed to be sublocal and not "leak" out of the comprehension) and the behaviour of assignment expressions (open to discussion).
Broadly speaking, there are two positions we can take:
- Let the current implementation of comprehensions as an implicit
hidden function drive the functionality; that means we duplicate the hairiness of the locals() behaviour seen above, although it won't be obvious at first glance.
What this means in practice is that assignments will go to different scopes depending on *where* they are in the comprehension:
[ expr for x in iter1 for y in iter2 if cond ...] [ BBBBBB for x in AAAAAA for y in BBBBBB if BBBBBB ...]
Assignments in the section marked "AAAAAA" will be in the local scope; assignments in the BBBBBB sections will be in the sublocal scope. That's not too bad, up to the point you try to assign to the same name in AAAAAA and BBBBBB. And then you are likely to get confusing hard to debug UnboundLocalErrors.
- Or we can keep the current behaviour for locals and the loop
variables, but we can keep assignment expressions simple by ensuring they always bind to the enclosing scope. Compared to the complexity of the above, we have the relatively straight forward:
[ AAAAAA for x in AAAAAA for y in AAAAAA if AAAAAA ...]
The loop variables continue to be hidden away in the invisible, implicit comprehension function, where they can't leak out, while explicit assignments to variables (using := or given or however it is spelled) will always go into the surrounding local scope, like they do in every other expression.
Does it matter that the implementation of this requires an implicit nonlocal declaration for each assignment? No more than it matters that comprehensions themselves require an implicit function.
And what we get out of this is simpler semantics at the Python level:
- Unless previous declared global, assignment expressions always bind to
the current scope, even if they're inside a comprehension;
- and we don't have to deal with the oddity that different bits of a
comprehension run in different scopes (unless we go out of our way to use locals()); merely using assignment expressions will just work consistently and simply, and loop variables will still be confined to the comprehension as they are now.
-- Steve _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/eric%2Ba-python-dev%40tru...