[Python-Dev] PEP 572 semantics
Tim Peters
tim.peters at gmail.com
Wed Jul 4 21:05:52 EDT 2018
[Steve Dower]
> In that case, please provide more examples of how it should work when
> the assignment expression appears to define a variable in a scope that
> is not on the call stack.
>
Sorry, I'm not clear what you're asking about. Python's scopes are
determined statically, at compile-time - they have nothing directly to do
with what's on the call stack at runtime. This PEP doesn't change anything
about that, either.
Whether intentional or not, there will be changes to how and when names
> are resolved.
Assignment expressions don't currently exist, so it's not possible to
change how their targets' names get resolved ;-) If the resolution of any
_other_ names got changed, that would be a bug, not an unintended
consequence.
> The specification should provide enough information to
> determine the preferred behaviour, so we can tell the difference between
> intention changes and implementation bugs.
>
> For example, what should be returned from this function?
>
Thanks! This clearly requires examples to flesh it out.
>
> >>> A = 0
> >>> def f(x):
> ... if x:
> ... [A := i for i in [1]]
> ... return A
>
> It depends on how it's called. `A` is local to `f` in any case (your
global `A` is wholly irrelevant here).. f(x) would raise UnboundLocalError
if `x` is not truthy, and return 1 if `x` is truthy.
> As far as I can tell, the closest current equivalent will not compile:
>
> >>> A = 0
> >>> def f(x):
> ... if x:
> ... def g():
> ... nonlocal A
> ... A = 1
> ... g()
> ... return A
> ...
> File "<stdin>", line 4
> SyntaxError: no binding for nonlocal 'A' found
>
That's because it's an incorrect translation: the "if the name is
otherwise unknown in the containing block, establish it as local in the
containing block" requires code in a "by hand" translation to force that to
be the case. "Otherwise unknown" means "is not declared global or nonlocal
in the block, and is not already known to be local to the block".
Because `A` isn't assigned to in the body of `f` outside the listcomp,
CPython _today_ has no idea `A` is intended to be local to `f`, so a
by-hand translation requires adding silly cruft to force `A` to be
recognized as local to `f`. Perhaps the easiest "covers all cases" way:
def f(x):
if 0: # NEW CODE HERE
A = None # AND HERE
if x:
# etc
That doesn't generate any code at all in CPython (the optimizer throws away
the entire "if 0:" block).. But compile-time analysis _does_ see the "A =
None" binding before the code is thrown away, and that's enough to
establish that `A` is local to `f`. Then it compiles fine today, and:
>>> f(0)
Traceback (most recent call last):
....
return A
UnboundLocalError: local variable 'A' referenced before assignment
>>> f(1)
1
> Is this the equivalent behaviour you want?
No, `A` is local to `f` - no compile-time error is appropriate here.
> Or do you want an
> UnboundLocalError when calling f(0)?
Yup, because `A` is local to `f` but not bound at the time it's referenced.
> Or do you want the global A to be returned?
If and only if there was a `global A` declaration in `f`.
> How should we approach decision making about these cases as we
> implement this? The PEP does not provide enough information for me to
> choose the right behaviour here, and I argue that it should.
>
> Well, as before, the PEP pretty clearly (to me) already says that `A` is
local to `f`. Everything else about the semantics follows from that. But
also as in an earlier reply, I think the PEP could help by providing some
worked-out workalike-function examples. The "if 0:" trick above isn't
deep, but it is too "clever" to be obvious. Then again, it's not required
to _specify_ the semantics, only to illustrate one possible way of
_implementing_ the semantics.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20180704/29b31a7a/attachment.html>
More information about the Python-Dev
mailing list