Variable Scopes in Python Classes

Python: what scopes are accessible when defining class variables?

From https://docs.python.org/3/reference/executionmodel.html:

Class definition blocks and arguments to exec() and eval() are special in the context of name resolution. A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope. This means that the following will fail:

In the definition of B, y is an unbound local variable, and so is looked up in the global scope (where it is not defined), not in the namespace created by the enclosing class statement.

A class statement does not define a scope at all; it creates a namespace which is passed to the metaclass in order to construct a new class.

Python and Variable Scope

In Python, variables declared inside the class definition, but not inside a method are class or static variables:

class TestB:
varNum = 0

This creates a class-level varNum variable, but this is distinct from any instance-level varNum variable, so you could have:

class TestB:
varNum = 0

def __init__(self):
self.varNum = 3

b = TestB()
print(b.varNum) # print 3
print(TestB.varNum) # print 0

Thus, class TestB should work in this way:

class TestB:
varNum = 0

def __init__(self):
self.varNum = 3

def printInstanceNum(self):
print(self.varNum)

def printClassNum():
print(TestB.varNum)

b = TestB()
b.printInstanceNum() # print 3
TestB.printClassNum() # print 0

Note that since there's no any reference to instance object in method printClassNum(), we don't have to put self as an argument. The method could actually become a staticmethod:

class TestB:
varNum = 0

@staticmethod
def printClassNum():
print(TestB.varNum)

Ref

  • class objects
  • static method

Confusion about scoping in python classes

As the tutorial said, scopes are searched in the order local, nonlocal, global, builtin.

The nonlocal scope is for enclosing functions. A class declaration is not a function. Its namespace gets thrown away after it is used to create the class object's __dict__, so variables at the class level cannot produce nonlocals in enclosed functions. Think of class-level assignments and variable reads like implicit assignments to and reads from a hidden dict, rather than as function locals. (Metaclasses can even replace this hidden dict with some other mapping.)

But the class scope does add one nonlocal, __class__. This is rarely used directly, but it's important for the zero-argument form of super().

This is the class object itself, so it's uninitialized until the class declaration finishes executing. So __class__.tricks would work inside a method if it's called after the class body executes (the usual case), but not if it's called during the execution of the class body.

There are other scopes to be aware of in Python. Comprehensions create a local scope like functions do. (They're basically compiled like generator functions--the kind with yield inside.) Also, caught exceptions are automatically deleted at the end of their handling clause to prevent a reference cycle.

You can see locals namespace using the locals() builtin and globals using globals(). The builtin scope is just the builtins module. The nonlocals are tricky. They'll actually show up in locals() if the compiler sees them being used. Function objects keep a reference to the nonlocals they use in their __closure__ attribute, which is a tuple of cells.

Python, scope of instance method variable in a class

self always refers to the actual instance of a class. So in your case even if you lazily declare a variable on self in a method, after it is declared it is accessible by any method bound to self, i.e. they share the same state.

There is no local instance scope in Python classes.

Python Variable Scope and Classes

When you put it right after class MyClass, it becomes a class attribute and you can get access to it via MyClass.my_var or as self.my_var from within the class (provided you don't create an instance variable with the same name).

Here's a little demo:

my_var = 'global'
class MyClass(object):
my_var = 'class'
def __init__(self):
print my_var #global
print MyClass.my_var #class
print self.my_var #class -- Only since we haven't set this attribute on the instance
self.my_var = 'instance' #set instance attribute.
print self.my_var #instance
print MyClass.my_var #class

Why is a class variable not accessible from a method?

The class body is not a nestable scope, no. The Python Execution Model explicitly excludes it:

The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods

That's because the body of a class is executed to form the class attributes; see it as a function with locals, and the locals become the attributes of the new class object.

You can then access those attributes either on the class directly (Foo.a) or via an instance (where attribute lookup falls through to the class).

Variable Scope In Generators In Classes

As stated in other answer, it is True that it happens because it is static variable. But it is not just that property that restricts your code to work. Actual reason is the scope of the variable and the scope in which it executes. For example, create a class as:

class A(object):
x = 999999
y = x +1

If you access it's class properties A.x and A.y, it will work. Because, at the time of initializing y, x is replaced the value in expression x+1. As the scope of x was within the class.

However this doesn't happens in the case of generators. i.e. in your example:

class A(object):
x = 4
gen = (x for _ in range(3))

When you do list(a.gen), it is executed outside the class (as generators are evaluated during run-time) and checks for the reference of x in the current scope. Since, x it is not initialized in that scope, it throws error.

When you explicitly initialize x=4, it works because now the generator expression has value of x to which it could use.

In order to make your generator expression work, as stated by others you have to define it like:

class A(object):
x = 4
gen = (A.x for _ in range(3))
# ^ mentioning `A.x` is the value to access

Python Class Variables' scope?

  1. Creation of an object (i.e. Emplyee() calls the __init__() method, the initializer). All methods that then have the self argument take that object's instance to operate on. Meaning these methods will have access to that object's self variables. This means that static methods do not have access to this, and why accessing those instance variables does not error. As an example, this would error:
class Employee:
def __init__(self):
self.variable = 123

@staticmethod # Decorator to indicate this method should not accept 'self'
def static_method():
print(self.variable)

Because the method static_method() does not operate on an initialize instance of the Employee object.


  1. Strings, like any other unassigned variable, will be interpreted and then discarded. Like the following:
123
"test"
[1, 2]

These all do nothing, but given that they're technically valid statements these do not result in errors. You will often see this type of statement in docstrings, like:

"""
Long string goes here.
"""

Class variable scope for static vs class methods

When decorated with @classmethod the first argument cls to inc_class(cls) is, well, the class. <class '__main__.A'> and <class '__main__.B'> respectively for A and B. So cls._var refers to A's _var, and similarly for B. In inc_static, decorated with @staticmethod there is no argument, you're explicitly referring to <class '__main__.Base'>, a different _var.

Note the '_var': 0 attribute in Base's and A's __dict__. @classmethod is doing what you'd expect it to do, binding members to classes, in this case A and B.

>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 0, 'inc_class': <classmethod
object at 0x7f23037a8b38>, 'inc_static': <staticmethod object at
0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base' objects>,
'__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None})

>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})`

After calling Base.inc_static():

>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class':
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base'
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>,
'__doc__': None})

>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})

After calling A.inc_class():

>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class':
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base'
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>,
'__doc__': None})

>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 1})

What's interesting is how A's _var is initialised. Note that you do cls._var += 1 before cls._var has been defined. As explained here, cls._var += 1 is equivalent to cls._var = cls._var; cls._var += 1. Because of the way python does lookup the first read of cls._var will fail in A and continue to find it in Base. At the assignment _var is added to A's __dict__ with the value of Base._var, and then all is fine.

>>> class Base(object):
... _var = 10
... @classmethod
... def inc_class(cls):
... cls._var += 1
...
>>> class A(Base):
... pass
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})
>>> A.inc_class()
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 11})


Related Topics



Leave a reply



Submit