Example:
class A:
a = 1
class B(A):
b = 2
y = b # works fine
x = a # NameError: name 'a' is not defined
x = A.a # works fine
z = B()
z.a # works fine
B.a # works fine
Why is x = a
not allowed? In every other context (access through an instance, access through the subclass name) it works fine; but somehow inside the class itself, it doesn't work.
And given this behavior, it seems I cannot implement a class hierarchy where each class defines some additional attributes - since I won't be able to access them in the subclass without knowing exactly where in the hierarchy they are defined.
Here's what I was trying (unsuccessfully) to do:
class X:
accelerate = compose(f1, f2, f3) # f1, f2, f3 are functions
class Y(X):
move = compose(f4, f5)
stop = f6
class Z(Y):
action = compose(accelerate, stop)
class U(Y):
action = compose(move, stop)
These classes wouldn't have been initialized at all; I just wanted to use them to create a hierarchy of functions.
When you write this:
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: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 classB
is actually created.This means that when Python comes to execute
x = a
, it findsa
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.
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 ofA
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:
This looks a little ugly, but constructs an identical class
B
as you would have if you createdx
inside the class block, and the transient classB
withoutx
can never be seen by any other code if you put theB.x
assignment directly after the class block. (It might not have identical results if you're using class decorators, though)