To better understand how inheritance works, we can look behind the scenes at how Python keeps track of instance variables and methods. Each class and instance of a class has some special instance variables that we can use to see how Python finds the right method to call or the right instance variable.
First, the _ _bases_ _
instance variable tells us the names of the superclass of any class. For example, in SESSION 12.3, you can see that the superclass of Line
is GeometricObject
. The parent of GeometricObject
is ABC
(Abstract Base Class). The parent of ABC
is object
, and object
has no parent. This succession of child-to-parent links is very important for Python when a method is called that was defined in a parent class. By default, Python uses the special class object
at the top of the inheritance hierarchy.
The name _ _bases_ _
comes from the term base class, which is an alternative way of thinking about the superclass–subclass relationship. Some programmers say that the superclass is the base class, and the subclass extends the base class. In our hierarchy, GeometricObject
is the base class of the Line
class. The critical idea is that Line
has all the functionality of GeometricObject
plus new functionality that extends the base class.
Another instance variable that each class maintains is the _ _dict_ _
variable. This instance variable is a dictionary that keeps track of the methods defined for the class. For example, in Session 12.3 you can see that the dictionary of methods for the Line
class has keys that correspond to the name of every method we defined.
Instances of Python objects also have some special instance variables. Every object has an instance variable called _ _class_ _
, which contains a reference to the object’s class. In Session 12.3, you can see that myLine._ _class_ _
refers to the Line
class. In addition, the _ _dict_ _
variable for instances of classes contains a dictionary of user-defined instance variables. For example, Session 12.3 shows that an instance of the Line
class also has a _ _dict_ _
instance variable that contains keys for _ _lineColor
and _ _lineWidth
that are inherited from the GeometricObject
class as well as _ _p1
and _ _p2
defined in the Line
class.
Putting all these special variables together, we can see in more detail how inheritance works in Python. FIGURE 12.5 shows a reference diagram that illustrates the chain of events in the Python interpreter for the expression myLine.getWidth()
.
When Python evaluates the expression myLine.getWidth()
, the first step is to dereference the name myLine
—that is, to find the object in memory. Python next tries to dereference the name getWidth
using the following chain of lookups.
If _ _dict_ _
in myLine
contains getWidth
as a key, stop searching and use it.
The method is not in myLine
, so follow the _ _class_ _
link to the Line
class.
If _ _dict_ _
in the Line
class contains getWidth
as a key, stop searching and use it.
The method is not in Line
, so follow the _ _bases_ _
link to the superclass (GeometricObject
).
If _ _dict_ _
contains getWidth
as a key, stop searching and use it.
In this case, we do find the getWidth
method in the GeometricObject
class.
Steps 5 and 6 can be repeated until all of the classes listed in _ _bases_ _
have been tried. If all base classes are exhausted and getWidth
is not found, then generate an error.
Once the name getWidth
is dereferenced, the next step is to apply the function call operators ()
. Since we are applying the call operators to a method of a class, we take the value of myLine
and pass it as the first parameter to the getWidth
function. In the getWidth
function, myLine
is called self
. FIGURE 12.6 illustrates the binding of myLine
as an actual parameter to the formal parameter self
. The main thing to notice is that myLine
and self
both reference the same instance of a Line
.
3.16.135.67