JeremyKun
JeremyKun

Reputation: 3002

Why is one class-inside-a-class not in the scope of another class-inside-a-class?

Consider the following code:

from enum import Enum

class A(object):
    class B(object):
        blah = "foo"
        derp = "bar"

    class SubA(object):
        def __init__(self):                                                                                                                                                                                                            
            self.value = B.derp

This raises a NameError

>>> A.SubA()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 10, in __init__
    self.value = B.derp
NameError: name 'B' is not defined

On the other hand, replacing B.derp with A.B.derp makes the code work as desired.

Why does Python's scoping resolve this in the way it does, and why should this make sense in Python's design?

Upvotes: 1

Views: 118

Answers (2)

C Panda
C Panda

Reputation: 3415

Although classes define a new namespace, they don't create a scope for names used inside of bodies of methods. While referring to them the names have to be fully qualified. That what self.something or cls.something are for.

Try using a fully qualified name like A.B.something. A is in the same namespace as B but not in the same variable scope.

Upvotes: 1

BrenBarn
BrenBarn

Reputation: 251608

As described in PEP 227, the class body's scope is not available from inside other scopes nested inside it. So the reason you get a NameError for B in your example is the same reason you get a NameError in this case:

class Foo(object):
    x = 88
    def foo(self):
        print(x)

Functions defined in the class body (i.e., methods) don't have access to the class body scope; neither do nested classes (or functions nested inside those).

A.B.derp works because A is in global scope, which is always accessible.

However, note that even A.B.derp won't work if you try to use it directly in the class body:

class A(object):
    class B(object):
        derp = 88
    class SubA(object):
        stuff = A.B.derp

This won't work because the line referring to A.B.derp is executed during the process of creating A, so A doesn't exist yet and can't be referenced.

Upvotes: 4

Related Questions