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:
... a = 1 ... a = (2 as a) ... print(a) ...
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.)