When you write this:
class B(A):
b = 2
y = b # works fine
x = a # NameError: name 'a' is not defined
x = A.a # works fine
What Python does is create a new scope (stored in a dictionary), execute all your definitions, then at the end of the class block it passes the dictionary to the type
class (unless you've set another metaclass) to create your new class. It's roughly equivalent to this:
B = type('B', (A,), classdict_from_scope)
Before you get to that stage, the class B
doesn't exist, let alone have any base classes to inherit attributes from. Consider that you could have multiple base classes, and the resolution order of names looked up in those classes is complex and depends on the full set of them and all their bases; this order isn't determined until your child class B
is actually created.
This means that when Python comes to execute x = a
, it finds a
not in scope, and it's not doing an attribute lookup on a class or instance, so it can't follow the name resolution protocol to look for an alternative binding. So all it can do is throw an error.
So that's why Python works that way. What can you do about it?
You have two basic options.
- You can specify the class you want to look for the attributes in explicitly.
- You can lookup the attributes in your subclass after it is created.
Note that (1) is not that bad if you're only using single inheritance; you can just look up all of the attributes in A
; if they're only defined in a parent class of A
this will still find them, so you don't actually need to know where in the hierarchy they are defined.
If you have multiple inheritance, then you would have to know at least which hierarchy contained the attribute you wanted to look up. If that's difficult or impossible, then you can use option (2), which would look something like this:
class B(A):
b = 2
y = b
B.x = B.a
This looks a little ugly, but constructs an identical class B
as you would have if you created x
inside the class block, and the transient class B
without x
can never be seen by any other code if you put the B.x
assignment directly after the class block. (It might not have identical results if you're using class decorators, though)