[Tutor] Odd behavior with eval, list comps, and name lookup

2011-02-18 Thread John
I noticed some odd behavior relating to eval(). First, a baseline case for
behavior:

 def test():
... x = 5
... return [a for a in range(10) if a == x]
...
 test()
[5]

So far so good. Now let's try eval:

 c = compile('[a for a in range(10) if a == x]', '', 'single')
 eval(c, globals(), {'x': 5})
Traceback (most recent call last):
File stdin, line 1, in module
File , line 1, in module
File , line 1, in listcomp
NameError: global name 'x' is not defined

Looks like 'x' is searched for only among the globals, bypassing the locals.
The same behavior is seen between exec and eval, with and without compile,
and between 3.1.3 and 3.2rc2. Given simpler code without a list comp, the
locals are used just fine:

 c = compile('a=5; a==x', '', 'single')
 eval(c, {}, {'x': 5})
True

Could anyone help me understand these scoping rules? Thanks.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Odd behavior with eval, list comps, and name lookup

2011-02-18 Thread Peter Otten
John wrote:

 I noticed some odd behavior relating to eval(). First, a baseline case for
 behavior:
 
 def test():
 ... x = 5
 ... return [a for a in range(10) if a == x]
 ...
 test()
 [5]
 
 So far so good. Now let's try eval:
 
 c = compile('[a for a in range(10) if a == x]', '', 'single')
 eval(c, globals(), {'x': 5})
 Traceback (most recent call last):
 File stdin, line 1, in module
 File , line 1, in module
 File , line 1, in listcomp
 NameError: global name 'x' is not defined
 
 Looks like 'x' is searched for only among the globals, bypassing the
 locals. The same behavior is seen between exec and eval, with and without
 compile, and between 3.1.3 and 3.2rc2. Given simpler code without a list
 comp, the locals are used just fine:
 
 c = compile('a=5; a==x', '', 'single')
 eval(c, {}, {'x': 5})
 True
 
 Could anyone help me understand these scoping rules? Thanks.

Except for class definitions the compiler statically determines whether a 
variable is global or local (or a closure).
List comprehensions and generator expressions are realized as functions; 
therefore they have their own local scope that you cannot provide via 
eval/exec. 

You could argue that in the following example

 exec(x = 2; print([a for a in range(3) if a == x]), {}, {})
Traceback (most recent call last):
  File stdin, line 1, in module
  File string, line 1, in module
  File string, line 1, in listcomp
NameError: global name 'x' is not defined

the assignment x = 2 should give the compiler a clue that x is supposed to 
be a local name, just like in

def f():
   x = 2
   print([a for a in range(3) if a == x])

My *guess* is that this is an implementation restriction rather than a 
conscious decision. It works for the common case of module-level code 
because there local and global namespace are identical:

 exec(x = 2; print([a for a in range(3) if a == x]), {})
[2]

Peter

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor