Hi Jonathon,

I'm going to try putting on a language lawyer hat here. It doesn't fit well, 
since I don't wear it very often. Nevertheless, here goes (speaking only about 
Python 2.6):

Generator expressions (the kind enclosed in parentheses) are creating a 
generator object. When you try to get at the information in a generator (in 
your example by using `tuple`), you use its `next` method. That is usually 
hidden from us thanks to Python syntax, but that `next` method is what's 
causing you grief. `xitem` and `yclass` don't get evaluated until we actually 
start using the generator. `xitem` is easy, because we've told the generator 
that it's coming from the values in `x`. `x` was already evaluated when you 
created the generator. But what about `yclass`? The generator doesn't have its 
own value for that variable, so it asks the system to use the next namespace up.

The problem is that classes have a simple namespace which isn't available to 
any old method. You ordinarily get to class variables from inside a method that 
has been defined for the class, and the generator function isn't one. The 
system knows this, so it's not even going to *try* asking the class. It's going 
to go straight to the global namespace, where there is no such variable as 
`yclass`.

List comprehensions (the kind enclosed in square brackets) don't have that 
problem, because they are building a list. The list comprehension lives in 
exactly the same namespace as `yclass`.

This link tells us about the lazy evaluation in generator expressions:

    http://docs.python.org/reference/expressions.html#generator-expressions

This link explains a little more about scoping and tells us that class 
variables can not be accessed as if they were simple local variables by any old 
code block. There's even an example very similar to yours as a demonstration of 
what won't work.

    http://docs.python.org/reference/executionmodel.html#naming-and-binding

The differences between generator expressions and list comprehensions can be a 
little confusing, especially when you start to add tricky details like class 
variable scoping. The really easy solution - which you seem to have discovered 
already - is like an old joke. 

    Patient: "It hurts when I do this. What do you recommend?"
    Doctor: "Don't do that."

I hope this explanation was at least a little helpful to you. It's a little 
muddy to me, to be honest. I just had to share these thoughts because I 
wouldn't have known at first glance that your code sample wouldn't work until I 
tried it.

Kind Regards,

Brian Wisti
http://coolnamehere.com

----- Original Message ----
From: Jonathan Mark <[email protected]>
To: [email protected]
Sent: Tuesday, June 23, 2009 1:19:16 PM
Subject: [SEAPY] question for language lawyer (generator expressions)

hi!
I was wondering if anyone knows why I get

NameError: global name 'yclass' is not defined

on this:

x = (1, 2, 3)
class C:
    yclass = 'hello'
    ex5 = tuple((xitem, yclass) for xitem in x)

It works fine if done at module scope instead of class scope.
It also works in Python 2.6.2 if the tuple() is changed to square brackets.  
However Python 3.0.1 rejects it either way.

A more comprehensive example is below... 2.6.2 rejects the ERR2 line and 3.0.1 
rejects both the ERR1 and ERR2 lines.

thanks for any clue you can provide... or is this a bug?

    Jonathan
--------------------------------- cut here -------------------------
x = (1, 2, 3)
ymod = 'hello'

ex1 = [(xitem, ymod) for xitem in x]
print('example 1:', ex1)

ex2 = tuple((xitem, ymod) for xitem in x)
print('example 2:', ex2)

class C:
    yclass = 'hello'

    ex3 = []
    for xitem in x:
        ex3.append((xitem, yclass))
    print('example 3:', ex3)

    ex4 = [(xitem, yclass) for xitem in x]      # -----ERR1-----
    print('example 4:', ex4)

    ex5 = tuple((xitem, yclass) for xitem in x) # -----ERR2-----
    print('example 5:', ex5)
--------------------------------- cut here -------------------------
Python 2.6.2 (r262:71600, Jun 23 2009, 12:32:53)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
('example 1:', [(1, 'hello'), (2, 'hello'), (3, 'hello')])
('example 2:', ((1, 'hello'), (2, 'hello'), (3, 'hello')))
('example 3:', [(1, 'hello'), (2, 'hello'), (3, 'hello')])
('example 4:', [(1, 'hello'), (2, 'hello'), (3, 'hello')])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 10, in <module>
    class C:
  File "test.py", line 21, in C
    ex5 = tuple((xitem, yclass) for xitem in x)    # -----ERR2-----
  File "test.py", line 21, in <genexpr>
    ex5 = tuple((xitem, yclass) for xitem in x)    # -----ERR2-----
NameError: global name 'yclass' is not defined
>>>

Reply via email to