Reputation: 174
I have following code, that doesn't work:
class Node:
n = 5
options = [ i / (n-1) for i in range(n)]
print("Success")
Error I am getting is:
Traceback (most recent call last):
File "/Users/ikkamens/Library/Preferences/PyCharm2019.2/scratches/counters.py", line 1, in <module>
class Node:
File "/Users/ikkamens/Library/Preferences/PyCharm2019.2/scratches/counters.py", line 3, in Node
options = [ i / (n-1) for i in range(n)]
File "/Users/ikkamens/Library/Preferences/PyCharm2019.2/scratches/counters.py", line 3, in <listcomp>
options = [ i / (n-1) for i in range(n)]
NameError: name 'n' is not defined
However, the following changed version works:
class Node:
n = 5
options = [ i / 4 for i in range(n)]
print("Success")
Why can I use the class level variable in range
expression, but not in (n-1)
? Is this an interpreter bug or is there some rule to explain this behavior? I have tried it with 3.8 and 3.6 interpreters.
Upvotes: 3
Views: 103
Reputation: 26005
There are two things here, as seen by the discussion under the OP.
First, in Python, there is no default object or class scope, such as the implicit this
in C++. So inside a function, your scope is that function, and you do not have "free" name access to variables defined on the object on the class. That is the reason we have to pass self
around - we need the environment containing our object (and class) explicitly. This means if we have
class A:
n = 5
we can only access n
in the class level definition, or by explicitly holding the environment:
class A:
n = 5
a = range(n) # legal, evaluated in the class definition, where 'A' is defined and 'n' is part of the statement.
def f():
x = self.n # legal. As long you do not override self.n same as next
x = A.n # legal, this is the class variable scope
x = n # Error - we do not have 'n' for free. Even worse, if 'n' is a global variable, we are using that.
Second, in Python 3, differently from Python 2 a list comprehension became scoped. No more leaking variables, and the body of the class
statement isn't really a scope (rather the definition of a namespace), and so not the scope where the anonymous function that implements the comprehension is defined. So
class A:
n = 5
x = [n/a for a in range(n)]
might be seen as
class A:
n = 5
def _anonymous_function(param):
return [n/a for a in range(param)]
x = _anonymous_function(n)
This way, it might be clearer why n
iss legal at one place, and not at the other. In the range(n)
, similar to the first example in the second code snippet in the answer, we are still in the class namespace definition statement, and "see" n
. On the other hand, in the comprehension body we are in a scoped (anonymous) environment, and do not see n
as it not in the outer (global) scope, since A
a namespace definition. This is the reason why it breaks here, as opposed to the other example. In Python 2 this would work.
Upvotes: 2
Reputation: 1310
I am not sure why its not work in this way but may be the interpreter think that
(n-1)
in the list is belong to a local variable inside the list or part of the loop.Although,I am not sure.
You can do this way
class Node:
global n
n = 5
options = [ i / (n-1) for i in range(n)]
print("Success")
Upvotes: 0