<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p>Is this a bug or a feature?<br>
Consider the following program:<br>
<br>
# TestProgram.py<br>
def Test():<br>
# global x<br>
x = 1<br>
exec(compile('print([x+1,x+2])', 'MyTest', 'exec'))<br>
exec(compile('print([x+i for i in range(1,3)])', 'MyTest',
'exec'))<br>
Test()<br>
<br>
In Python 2.7.15 the output is<br>
<br>
[2, 3]<br>
[2, 3]<br>
<br>
In Python 3.6.5 the output is<br>
[2, 3]<br>
Traceback (most recent call last):<br>
File "TestProgram.py", line 7, in <module><br>
Test()<br>
File "TestProgram.py", line 6, in Test<br>
exec(compile('print([x+i for i in range(1,3)])', 'MyTest',
'exec'))<br>
File "MyTest", line 1, in <module><br>
File "MyTest", line 1, in <listcomp><br>
NameError: name 'x' is not defined<br>
<br>
If the "global x" declaration is uncommented, this "fixes" the
Python 3.6.5 behaviour,<br>
i.e. no error occurs and the output is the same as for Python
2.7.15.<br>
<br>
<b>In other words, it looks as if in Python 3.6.5, the compiled
list comprehension</b><b><br>
</b><b>can "see" a pre-existing global variable but not a local
one.</b><br>
<br>
I have used dis to examine the code objects returned by compile()<br>
(they are the same with or without the "global x"):<br>
<br>
Python 2.7.15 first code object from 'print([x+1,x+2])':<br>
1 0 LOAD_NAME 0 (x)<br>
3 LOAD_CONST 0 (1)<br>
6 BINARY_ADD<br>
7 LOAD_NAME 0 (x)<br>
10 LOAD_CONST 1 (2)<br>
13 BINARY_ADD<br>
14 BUILD_LIST 2<br>
17 PRINT_ITEM<br>
18 PRINT_NEWLINE<br>
19 LOAD_CONST 2 (None)<br>
22 RETURN_VALUE<br>
Python 2.7.15 second code object from 'print([x+i for i in
range(1,3)])':<br>
1 0 BUILD_LIST 0<br>
3 LOAD_NAME 0 (range)<br>
6 LOAD_CONST 0 (1)<br>
9 LOAD_CONST 1 (3)<br>
12 CALL_FUNCTION 2<br>
15 GET_ITER<br>
>> 16 FOR_ITER 16 (to 35)<br>
19 STORE_NAME 1 (i)<br>
22 LOAD_NAME 2 (x)<br>
25 LOAD_NAME 1 (i)<br>
28 BINARY_ADD<br>
29 LIST_APPEND 2<br>
32 JUMP_ABSOLUTE 16<br>
>> 35 PRINT_ITEM<br>
36 PRINT_NEWLINE<br>
37 LOAD_CONST 2 (None)<br>
40 RETURN_VALUE<br>
Python 3.6.5 first code object from 'print([x+1,x+2])':<br>
1 0 LOAD_NAME 0 (print)<br>
2 LOAD_NAME 1 (x)<br>
4 LOAD_CONST 0 (1)<br>
6 BINARY_ADD<br>
8 LOAD_NAME 1 (x)<br>
10 LOAD_CONST 1 (2)<br>
12 BINARY_ADD<br>
14 BUILD_LIST 2<br>
16 CALL_FUNCTION 1<br>
18 POP_TOP<br>
20 LOAD_CONST 2 (None)<br>
22 RETURN_VALUE<br>
Python 3.6.5 second code object from 'print([x+i for i in
range(1,3)])':<br>
1 0 LOAD_NAME 0 (print)<br>
2 LOAD_CONST 0 (<code object
<listcomp> at 0x00000000029F79C0, file "MyTest", line 1>)<br>
4 LOAD_CONST 1 ('<listcomp>')<br>
6 MAKE_FUNCTION 0<br>
8 LOAD_NAME 1 (range)<br>
10 LOAD_CONST 2 (1)<br>
12 LOAD_CONST 3 (3)<br>
14 CALL_FUNCTION 2<br>
16 GET_ITER<br>
18 CALL_FUNCTION 1<br>
20 CALL_FUNCTION 1<br>
22 POP_TOP<br>
24 LOAD_CONST 4 (None)<br>
26 RETURN_VALUE<br>
<br>
You will see that in Python 3.6.5 the dis output for the second
code object<br>
does not show the internals of the listcomp, and in particular
whether,<br>
and how, it refers to the variable 'x'. I don't know how to
investigate further.<br>
<br>
Best wishes<br>
Rob Cliffe<br>
</p>
</body>
</html>