The whole point of a namespace tool like the class
statement is to support name
inheritance. In Python,
inheritance happens when an object is qualified, and involves
searching an attribute definition tree (one or more namespaces).
Every time you use an expression of the form
object.attr
where object is an instance or class
object, Python searches the namespace tree at and above
object
, for the first attr
it
can find. Because lower definitions in the tree override higher ones,
inheritance forms the basis of specialization.
Figure 6.4 sketches the way namespace trees are constructed. In general:
Instance attributes are generated by
assignments to self
attributes in methods.
Class attributes are created by statements
(assignments) in class
statements.
Superclass links are made by listing classes in
parentheses in a class
statement header.
The net result is a tree of attribute namespaces, which grows from an instance, to the class it was generated from, to all the superclasses listed in the class headers. Python searches upward in this tree from instances to superclasses, each time you use qualification to fetch an attribute name from an instance object.[46]
The tree-searching model of inheritance we just described turns out to be a great way to specialize systems. Because inheritance finds names in subclasses before it checks superclasses, subclasses can replace default behavior by redefining the superclass’s attributes. In fact, you can build entire systems as hierarchies of classes, which are extended by adding new external subclasses rather than changing existing logic in place.
The idea of overloading inherited names leads to a variety of specialization techniques. For instance, subclasses may replace inherited names completely, provide names a superclass expects to find, and extend superclass methods by calling back to the superclass from an overridden method. We’ve already seen replacement in action; here’s an example that shows how extension works:
>>>class Super:
...def method(self):
...print 'in Super.method'
...>>>
class Sub(Super):
...def method(self):
# override method ...print 'starting Sub.method'
# add actions here ...Super.method(self)
# run default action ...print 'ending Sub.method'
...
Direct superclass method calls are the crux of the matter here. The
Sub
class replaces
Super
’s method
function
with its own specialized version. But within the replacement,
Sub
calls back to the version exported by
Super
to carry out the default behavior. In other
words, Sub.method
just extends
Super.method
’s behavior, rather than replace
it completely:
>>>x = Super()
# make a Super instance >>>x.method()
# runs Super.method in Super.method >>>x = Sub()
# make a Sub instance >>>x.method()
# runs Sub.method, which calls Super.method starting Sub.method in Super.method ending Sub.method
Extension
is commonly used with constructors; since the specially named
__ init
__
method is an inherited name, only one
is found and run when an instance is created. To run superclass
constructors, subclass
__ init
__
methods should call superclass
__ init
__
methods, by qualifying classes (e.g., Class.
__ init
__ (self,
...)
).
Extension is only one way to interface with a superclass; the following shows subclasses that illustrate these common schemes:
Super
defines a method
function and a delegate
that expects an
action
in a subclass.
Inheritor
doesn’t provide any new names, so
it gets everything defined in Super
.
Replacer
overrides
Super
’s method
with a
version of its own.
Extender
customizes
Super
’s method
by
overriding and calling back to run the default.
Provider
implements the action
method expected by Super
’s
delegate
method.
class Super: def method(self): print 'in Super.method' # default def delegate(self): self.action() # expected class Inheritor(Super): pass class Replacer(Super): def method(self): print 'in Replacer.method' class Extender(Super): def method(self): print 'starting Extender.method' Super.method(self) print 'ending Extender.method' class Provider(Super): def action(self): print 'in Provider.action' if __name__ == '__main__': for klass in (Inheritor, Replacer, Extender): print ' ' + klass.__name__ + '...' klass().method() print ' Provider...' Provider().delegate()
A few things are worth pointing out here: the self-test code at the
end of this example creates instances of three different classes;
because classes are objects, you can put them in a tuple and create
instances generically (more on this idea later). Classes also have
the special
__ name
_
_
attribute as modules; it’s just preset to
a string containing the name in the class header. When you call the
delegate
method though a
Provider
instance, Python finds the
action
method in Provider
by
the usual tree search: inside the Super
delegate
method, self
references a Provider
instance.
% python specialize.py
Inheritor...
in Super.method
Replacer...
in Replacer.method
Extender...
starting Extender.method
in Super.method
ending Extender.method
Provider...
in Provider.action
[46] This
description isn’t 100% complete, because instance and class
attributes can also be created by assigning to objects outside
class
statements. But that’s less common and
sometimes more error prone (changes aren’t isolated to
class
statements). In Python all attributes are
always accessible by default; we talk about privacy later in this
chapter.