scope of generators, class variables, resulting in global na

Alf P. Steinbach alfps at start.no
Wed Feb 24 07:21:31 EST 2010


* Nomen Nescio:
> Hello,
> 
> Can someone help me understand what is wrong with this example?
> 
> class T:
>   A = range(2)
>   B = range(4)
>   s = sum(i*j for i in A for j in B)
> 
> It produces the exception:
> 
> <type 'exceptions.NameError'>: global name 'j' is not defined

Which Python implementation are you using?

I can't reproduce the error message that you cite.


<example>
C:\test> py2
Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on 
win32
Type "help", "copyright", "credits" or "license" for more information.
 >>> class T:
...   A = range(2)
...   B = range(4)
...   s = sum(i*j for i in A for j in B)
...
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "<stdin>", line 4, in T
   File "<stdin>", line 4, in <genexpr>
NameError: global name 'B' is not defined
 >>> exit()

C:\test> py3
Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on 
win32
Type "help", "copyright", "credits" or "license" for more information.
 >>> class T:
...   A = range(2)
...   B = range(4)
...   s = sum(i*j for i in A for j in B)
...
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "<stdin>", line 4, in T
   File "<stdin>", line 4, in <genexpr>
NameError: global name 'B' is not defined
 >>> exit()

C:\test> _
</example>

Reason for the NameError:

The above is a /generator expression/, evaluated in a class definition.

The docs have a similar example but I'm sorry, I'm unable to find it! Anyway the 
generator expression is evaluated as if its code was put in function. And from 
within the scope of that function you can't access the class scope implicitly, 
hence, no access to 'B'.



> The exception above is especially confusing since the following similar example
> (I just replaced the generator by an explicit array) works:
> 
> class T:
>   A = range(2)
>   B = range(4)
>   s = sum([(i*j) for i in A for j in B])
> 
> (BTW, the class scope declarations are intentional).
> 
> Thanks, Leo.

This is a /list comprehension/, not a generator expression (although 
syntactically it's almost the same).

It obeys different rules.

Essentially the generator expression produces a generator object that you may 
name or pass around as you wish, while the comprehension is just a syntactical 
device for writing more concisely some equivalent code that's generated inline.

However, apparently the rules changed between Python 2.x and Python 3.x.

In Python 3.x also the list comprehension fails in a class definition:


<example>
C:\test> py2
Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on 
win32
Type "help", "copyright", "credits" or "license" for more information.
 >>> class T:
...   A = range(2)
...   B = range(4)
...   s = sum([(i*j) for i in A for j in B])
...
 >>> exit()

C:\test> py3
Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on 
win32
Type "help", "copyright", "credits" or "license" for more information.
 >>> class T:
...   A = range(2)
...   B = range(4)
...   s = sum([(i*j) for i in A for j in B])
...
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "<stdin>", line 4, in T
   File "<stdin>", line 4, in <listcomp>
NameError: global name 'B' is not defined
 >>> exit()

C:\test> _
</example>


 From one point of view it's good that Py3 provides about the same behavior for 
generator expressions and list comprehensions.

But I'd really like the above examples to Just Work. :-)


Cheers & hth.,

- Alf



More information about the Python-list mailing list