We introduced operator overloading at the start of this chapter; let’s fill in a few blanks here and look at a handful of commonly used overloading methods. Here’s a review of the key ideas behind overloading:
Operator overloading lets classes intercept normal Python operations.
Classes can overload all Python expression operators.
Classes can also overload object operations: printing, calls, qualification, etc.
Overloading makes class instances act more like built-in types.
Overloading is implemented by providing specially named class methods.
Here’s a simple example of overloading at work. When we provide
specially named methods in a class, Python automatically calls them
when instances of the class appear in the associated operation. For
instance, the Number
class below provides a method
to intercept instance construction (
__init
__
), as well as one for catching
subtraction expressions (
__ sub
__
). Special methods are the hook that
lets you tie into built-in operations:
class Number: def __init__(self, start): # on Number(start) self.data = start def __sub__(self, other): # on instance - other return Number(self.data - other) # result is a new instance >>>from number import Number
# fetch class from module >>>X = Number(5)
# calls Number.__init__(X, 5) >>>Y = X - 2
# calls Number.__sub__(X, 2) >>>Y.data
3
Just about everything you can do to built-in objects such as integers and lists has a corresponding specially named method for overloading in classes. Table 6.1 lists a handful of the most common; there are many more than we have time to cover in this book. See other Python books or the Python Library Reference Manual for an exhaustive list of special method names available. All overload methods have names that start and end with two underscores, to keep them distinct from other names you define in your classes.
Table 6-1. A Sampling of Operator Overloading Methods
Let’s illustrate a few of the methods in Table 6.1 by example.
The
__getitem
__ method intercepts instance indexing operations:
When an instance X
appears in an indexing
expression like X[i]
, Python calls a __getitem
__
method inherited by the
instance (if any), passing X
to the first argument
and the index in brackets to the second argument. For instance, the
following class returns the square of index values:
>>>class indexer:
...def
__getitem
__(self, index):
...return index ** 2
... >>>X = indexer()
>>>for i in range(5):
...print X[i],
# X[i] calls __getitem__(X, i) ... 0 1 4 9 16
Now, here’s a special trick that isn’t always obvious to
beginners, but turns out to be incredibly useful: when we introduced
the for
statement back in Chapter 3, we mentioned that it works by repeatedly
indexing a sequence from zero to higher indexes, until an
out-of-bounds exception is detected. Because of that,
__ getitem
__
also
turns out to be the way to overload iteration and membership tests in
Python. It’s a case of “buy one, get two free”: any
built-in or user-defined object that responds to indexing also
responds to iteration and membership automatically:
>>>class stepper:
...def
__getitem
__(self, i):
...return self.data[i
] ... >>>X = stepper()
# X is a stepper object >>>X.data = "Spam"
>>> >>>for item in X:
# for loops call __getitem__ ...print item,
# for indexes items 0..N ... S p a m >>> >>>'p' in X
# 'in' operator calls __getitem__ too 1
The
__getattr
__ method
intercepts attribute qualifications. More specifically, it’s
called with the attribute name as a string, whenever you try to
qualify an instance on an undefined (nonexistent) attribute name.
It’s not called if Python can find the attribute using its
inheritance tree-search procedure. Because of this behavior, _
_getattr
__ is useful as a hook for responding to
attribute requests in a generic fashion. For example:
>>>class empty:
...def
__getattr
__(self, attrname):
...if attrname == "age":
...return 36
...else:
...raise AttributeError, attrname
... >>>X = empty()
>>>X.age
36 >>>X.name
Traceback (innermost last): File "<stdin>", line 1, in ? File "<stdin>", line 6, in __getattr__ AttributeError: name
Here, the empty
class and its instance
X
have no real attributes of their own, so the
access to X.age
gets routed to the
__ getattr
__
method; self
is assigned the instance
(X
), and attrname
is assigned
the undefined attribute name string ("age"
). Our
class makes age
look like a real attribute by
returning a real value as the result of the X.age
qualification expression (36
).
For other attributes the class doesn’t know how to handle, it
raises the built-in AttributeError
exception, to
tell Python that this is a bona fide undefined name; asking for
X.name
triggers the error. We’ll see
__ getattr
__
again when we show delegation at work, and we will say
more about exceptions in Chapter 7.
Here’s an example that exercises the
_
_ init
__
constructor and the
__ add
__
+
overload methods we’ve already
seen, but also defines a
__ repr
__
that returns a string
representation of instances. Backquotes are used to convert the
managed self.data
object to a string.
If defined,
__ repr
__
is called automatically when class
objects are printed or converted to strings.
>>>class adder:
...def
__init
__(self, value=0):
...self.data = value
# initialize data ...def
__add
__(self, other):
...self.data = self.data + other
# add other in-place ...def
__repr
__(self):
...return `self.data`
# convert to string ... >>>X = adder(1)
# __init__ >>>X + 2; X + 2
# __add__ >>>X
# __repr__ 5
That’s as many overloading examples as we have space for here. Most work similarly to ones we’ve already seen, and all are just hooks for intercepting built-in type operations we’ve already studied; but some overload methods have unique argument lists or return values. We’ll see a few others in action later in the text, but for a complete coverage, we’ll defer to other documentation sources.