Credit: Alex Martelli
You’d like to inherit from a built-in type, but you are using Python 2.1 (or earlier), or need a semantic detail of classic classes that would be lost by inheriting from a built-in type in Python 2.2.
With Python 2.2, we can inherit directly from a built-in type. For
example, we can subclass file
with our own
new-style class and override some methods:
class UppercaseFile(file): def write(self, astring): return file.write(self, astring.upper( )) def writelines(self, strings): return file.writelines(self, map(string.upper,strings)) upperOpen = UppercaseFile
To open such a file, we can call
upperOpen
just like a function, with the same arguments as the built-in
open
function. Because we don’t
override _ _init_ _
, we inherit
file
’s arguments, which are the
same as open
’s.
If we are using Python 2.1 or earlier, or if we need a classic class for whatever purpose, we can use automatic delegation:
class UppercaseFile:
# Initialization needs to be explicit
def _ _init_ _(self, file):
# NOT self.file=file, to avoid triggering _ _setattr_ _
self._ _dict_ _['file'] = file
# Overrides aren't very different from the inheritance case:
def write(self, astring):
return self.file.write(astring.upper( ))
def writelines(self, strings):
return self.file.writelines(map(string.upper,strings))
# Automatic delegation is a simple and short boilerplate:
def _ _getattr_ _(self, attr):
return getattr(self.file, attr)
def _ _setattr_ _(self, attr, value):
return setattr(self.file, attr, value)
def upperOpen(*args, **kwds):
return UppercaseFile(open(*args, **kwds))
In this variant, upperOpen
is called just as
before but it separates the generation of the file object internally
(done via the built-in open
function) and its
wrapping into the automatically delegating class
(UppercaseFile
).
Automatic delegation, which the special methods _ _getattr
and _ _setattr_ _
let us
perform so smoothly, is a powerful and general technique. In this
recipe, we show how to use it to get an effect that is almost
indistinguishable from subclassing a built-in type, but in a way that
also works with Python 2.1 and earlier. This technique also produces
a classic class, just in case we want the classic object
model’s semantics even in newer versions of Python.
Performance isn’t quite as good as with real
inheritance, but we get better flexibility and finer-grained control
as compensation.
The fundamental idea is that each instance of our class holds an
instance of the type we are wrapping (i.e., extending and/or
tweaking). Whenever client code tries to get an attribute from an
instance of our class, unless the attribute is specifically defined
there (e.g., the write
and
writelines
methods in this recipe), _ _getattr_ _
transparently shunts the request to the wrapped
instance. In
Python, methods are also
attributes, accessed in just the same way, so we
don’t need to do anything more to access
methods—the approach used to access data attributes works for
methods just as well. _ _setattr_ _
plays a
similar role when client code sets an attribute. Remember that to
avoid triggering _ _setattr_ _
from inside the
methods you code, you must set values in self._ _dict_ _
explicitly. While Python calls _ _getattr_ _
only for attributes it does not find in the usual way, it
calls _ _setattr_ _
for every attribute that is
set (except for a few special ones such as _ _dict_ _
and _ _class_ _
, held in the object
itself and not in its dictionary).
Note that wrapping by automatic delegation does not work well with client or framework code that, one way or another, does type-testing. In such cases, it is the client or framework code that is breaking polymorphism and should be rewritten. Remember not to use type-tests in your own client code, as you probably do not need them anyway. See Recipe 5.11 for better alternatives.
In Python 2.2, you’ll use automatic delegation less
often, since you don’t need it for the specific
purpose of subclassing built-ins. However, delegation still has its
place—it is just a bit farther from the spotlight than in 2.1
and earlier. Although the new-style object model (which you get by
subclassing built-ins) is almost always preferable, there are a few
cases in which you should use classic classes because they are even
more dynamic than new-style classes. For example, if your program
needs to change an instance’s _ _class_ _
on the fly, this is always allowed for instances of
classic classes, but subject to constraints for instances of
new-style classes. More importantly, delegation is generally more
flexible than inheritance, and sometimes such flexibility is
invaluable. For example, an object can delegate to different
subobjects over time or even all at once (see Recipe 5.21), and inheritance doesn’t
offer anything comparable.
Recipe 5.11 and Recipe 5.21; PEP 253 (http://www.python.org/peps/pep-0253.html) describes in detail what there is to know about subtyping built-in types.
Get Python Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.