1
votes

Consider these two python files:

# file1.py
global_var = "abc"

class A:
    x = 1
    glb = global_var
    y = x + 1
    class B:
        z = 3
        glb = global_var
    zz = B.z

print(f"{A.B.z=}")
print(f"{A.zz=}")
# file2.py
global_var = "abc"

class A:
    x = 1
    glb = global_var
    y = x + 1
    class B:
        z = y + 1
        glb = global_var
    zz = B.z

print(f"{A.B.z=}")
print(f"{A.zz=}")

One would expect them to do the exact same thing. But they don't!

$ python file1.py
A.B.z=3
A.zz=3
$ python file2.py
Traceback (most recent call last):
  File "file2.py", line 4, in <module>
    class A:
  File "file2.py", line 8, in A
    class B:
  File "file2.py", line 9, in B
    z = y + 1
NameError: name 'y' is not defined

Questions:

  • Why can the definition of B access the global scope, but not the scope of A?
  • Why is it that y = x + 1 should work but z = y + 1 should not? Is this a design decision, or undefined behavior of CPython?
  • What are the general rules for what variables / scope are accessible when calculating the values of class variables? When should I worry about which scope I'm allowed to use in defining my class variables?
2
This happens for the same reason you have to do self.x or ClassName.x to access a class variable x in a method.user2357112 supports Monica
I belive (forget where this is documented) that all lookups in a class statement occur in the global scope, not the closest containing scope. (In the case of B, the global scope is the closest containing scope anyway, because a class statement does not create a new scope.) y isn't defined, because the definition of B doesn't look in the definition of A.chepner
@chepner That Is what I would expect, but printing locals().keys() within definitions of A and B does show the respective class variables in the local scope. And actually, it has to, when you think of it, otherwise all class variables would end up in the module scope. So there is a class scope, but it is a temporary scope, which exists only while building the class.zvone
No, there is a class namespace; that's not the same as a scope. locals is returning that namespace, but that doesn't mean name resolution uses it.chepner
Relevant part from PEP 227 is Names in class scope are not accessible. Names are resolved in the innermost enclosing function scope. If a class definition occurs in a chain of nested scopes, the resolution process skips class definitions.wim

2 Answers

1
votes

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.

1
votes

Answers:

  • Why can the definition of B access the global scope, but not the scope of A?

Chepner Already answer this one, but in short: Local variables are looked up in the global namespace so the way the classes are nested in your code sample work as expected.

  • Why is it that y = x + 1 should work but z = y + 1 should not? Is this a design decision, or undefined behavior of CPython?

It works as expected as stated above.


  • What are the general rules for what variables / scope are accessible when calculating the values of class variables? When should I worry about which scope I'm allowed to use in defining my class variables?

They don't work as for nested functions with the LEGB rule /realpython:

"When you define a class, you’re creating a new local Python scope. The names assigned at the top level of the class live in this local scope. The names that you assigned inside a class statement don’t clash with names elsewhere. You can say that these names follow the LEGB rule, where the class block represents the L level."

Also is not very common to nest a Class within another Class, normally you do Class inheritance as so:

class B(A):
    y = A.y
    z =  y  + 1
    glb = global_var