On 02/27/2018 09:23 PM, Chris Angelico wrote:
On Wed, Feb 28, 2018 at 2:47 PM, Rob Cliffe via Python-ideas wrote:

And here's a thought: What are the semantics of
     a = (42 as a) # Of course a linter should point this out too
At first I thought this was also a laborious synonym for "a=42".  But then I
re-read your statement (the one I described above as crystal-clear) and
realised that its exact wording was even more critical than I had thought:
     "the new name binding will shadow the other name from the point where it
is evaluated until the end of the statement"
Note: "until the end of the statement".  NOT "until the end of the
expression".  The distinction matters.
If we take this as gospel, all this will do is create a temporary variable
"a", assign the value 42 to it twice, then discard it.  I.e. it effectively
does nothing, slowly.
Have I understood correctly?  Very likely you have considered this and mean
exactly what you say, but I am sure you will understand that I mean no
offence by querying it.

Actually, that's a very good point, and I had to actually go and do
that to confirm. You're correct that the "a =" part is also affected,
but there may be more complicated edge cases. Disassembly can help
track down what the compiler's actually doing:

def f():
...     a = 1
...     a = (2 as a)
...     print(a)
...
dis.dis(f)
   2           0 LOAD_CONST               1 (1)
               2 STORE_FAST               0 (a)

   3           4 LOAD_CONST               2 (2)
               6 DUP_TOP
               8 STORE_FAST               1 (a)
              10 STORE_FAST               1 (a)
              12 DELETE_FAST              1 (a)

   4          14 LOAD_GLOBAL              0 (print)
              16 LOAD_FAST                0 (a)
              18 CALL_FUNCTION            1
              20 POP_TOP
              22 LOAD_CONST               0 (None)
              24 RETURN_VALUE

If you're not familiar with the output of dis.dis(), the first column
(largely blank) is line numbers in the source, the second is byte code
offsets, and then we have the operation and its parameter (if any).
The STORE_FAST and LOAD_FAST opcodes work with local names, which are
identified by their indices; the first such operation sets slot 0
(named "a"), but the two that happen in line 3 (byte positions 8 and
10) are manipulating slot 1 (also named "a"). So you can see that line
3 never touches slot 0, and it is entirely operating within the SLNB
scope.

dis.dis may be great, but so is running the function so everyone can see the 
output.  ;)

If I understood your explanation, `print(a)` produces `1` ? That seems wrong -- the point of statement-local name bindings is twofold:

- give a name to a value
- evaluate to that value

Which is why your first example works:

    stuff = [[(f(x) as y), y] for x in range(5)]

(f(x) as y), y

evaluates as f(x), and also assigns that result to y, so in

    a = (2 as a)

there is a temporary variable 'a', which gets assigned 2, and the SLNB is evaluated as 2, which should then get assigned back to the local variable 'a'. In other words, the final print from `f()` above should be 2, not 1. (Slightly different names would help avoid confusion when referencing different locations of the PEP.)

--
~Ethan~
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to