1

Here is some code that raise a NameError:

class Foo:
    a = [1,2,3,4,5]
    b = all(i for i in a) # works fine !
    c = [2,3,4]
    d = all(i in a for i in c) # raises NameError : name 'a' is not defined

I am quite confused by that --;

Thanks !

Vince
  • 3,979
  • 10
  • 41
  • 69

2 Answers2

6

See this previous question, which is similar but not identical.

As stated in that answer:

classes don't have a lexical scope (actually, in either Python 2 or Python 3). Instead, they have a local namespace that does not constitute a scope. This means that expressions within the class definition have access to the content of the namespace [...] but scopes introduced within the body of the class do not have access to its namespace

In a generator expression, the for clause is in the enclosing scope/namespace, but the target expression is in a new scope created by the generator expression. This new scope doesn't have access to the enclosing class scope.

In short, your examples work and fail for the same reason that this works:

class Foo(object):
    a = 2
    def method(self, x=a):
        print x

but this fails:

class Foo(object):
    a = 2
    def method(self, x):
        print a

The for clause of the generator expression is similar scopewise to the argument specification of a method: it executes in the enclosing namespace. But the target expression of the genexp is similar to the method body: it executes in its own scope, which does not have access to the class namespace.

(In Python 2, using a list comprehension instead of a generator comprehension will work because list comprehensions do not createa new scope in Python 2. This was considered a wart and was changed in Python 3, so in Python 3 neither one will work.)

Community
  • 1
  • 1
BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • @asad in this example yes, but not in the OP's code – David Robinson Mar 28 '14 at 04:52
  • @Asad: In this example, yes, but that's getting into instance attributes which aren't part of the original question. Using `self.a` wouldn't work in a class-level genexp as in the question. – BrenBarn Mar 28 '14 at 04:52
  • @BrenBarn Okay, one last question if someone wouldn't mind answering: do the attributes you're defining for the class get added to the enclosing scope (i.e. the scope you're defining the class in) as variables? Because otherwise I don't understand why the list comprehension would work, despite not creating an additional scope. Thanks. – Asad Saeeduddin Mar 28 '14 at 04:54
  • 1
    @Asad: They don't get added to the enclosing scope (i.e., the global scope), but they are added to the class namespace. This is the "namespace that does not constitute a scope" referred to in the bit I quoted from the answer to the other question. – BrenBarn Mar 28 '14 at 04:56
1

The real answer to this question is what BrenBarn wrote.

In Python 2.7 what you wrote didn't work but if you change it to:

class Foo:
    a = [1,2,3,4,5]
    b = all(i for i in a) # works fine !
    c = [2,3,4]
    d = all([i in a for i in c]) #works fine too!

it will work as expected. The reason being as pointed out by BrenBarn that list comprehensions do not create a new scope in Python 2.

carlosdc
  • 12,022
  • 4
  • 45
  • 62
  • That's odd. I would assume this would still raise the NameError. edit: I stand corrected. Mind explaining how this works? – Asad Saeeduddin Mar 28 '14 at 04:48
  • It is odd indeed. The lack of [] struck me as suspect and I was drawn to trying that first. I don't understand what is going on. Will go through the other answers. – carlosdc Mar 28 '14 at 04:49
  • See my answer. This will work in Python 2 but not Python 3. It works because in Python 2 list comprehensions, unlike generator comprehensions, do not create their own scope. – BrenBarn Mar 28 '14 at 04:51