Certain method names have special meaning to Python;
each of these special method names starts with two underbars, “__”.
A class's constructor method, __init__(),
is an example of a special method. Whenever you use
the class's name as if it were a function, in an
expression like “SnailRun('Judy',
67.3)”, Python executes the constructor
method to build the new instance.
There is a full list of all the Python special method names in
the Python quick reference. Next we will
look at another special method, __cmp__, that
Python calls whenever you compare two instances of that class.
Going back to our snail-racing application, an instance of the
SnailRun class contains everything we need
to know about one snail's performance: its name in the
.name attribute and its finish time in the
.time attribute.
However, using the tuple representation back in Section 11.4, “Snail-oriented data structures: A list of tuples”, we were able to put a
collection of these tuples into a list, and sort the list
so that they were ordered by finish time, with the winner
first. Let's see what we need to add to class
SnailRun so that we can sort a list of them into
finish order by calling the .sort() method
on the list.
First, a bit of review. Back in Section 6.1, “Conditions and the bool type”, we learned about the built-in Python function cmp(, which returns:
x, y)
a negative number if is less than
x;
y
a positive number if is greater than
x; or
y
zero if
equals x.
y
In a Python class, if you define a method named
“__cmp__”, that method is
called whenever Python compares two instances of the
class. It must return a result using the same
conventions as the built-in cmp()
function: negative for “<”, zero for “==”,
positive for “>”.
In the case of “class SnailRun”, we want the snail with the better finishing
time to be considered less than the slower snail. So
here is one way to define the __cmp__
method for our class:
def __cmp__ ( self, other ):
"""Define how to compare two SnailRun instances.
"""
if self.time < other.time:
return -1
elif self.time > other.time:
return 1
else:
return 0
When this method is called, self is an
instance of class SnailRun, and other should also be an instance of SnailRun.
However, this logic exactly duplicates what the built-in
cmp() function does to compare two
float values, so we can simplify it like this:
def __cmp__ ( self, other ):
"""Define how to compare two SnailRun instances.
"""
return cmp(self.time, other.time)
Let's look at another special method, __str__(). This one defines how Python converts
an instance of a class into a string. It is called, for
example, when you name an instance in a print statement, or when you pass an instance to
Python's built-in str() function.
The __str__() method of a class returns a
string value. It is up to the writer of the class what
string value gets returned. As usual for Python methods,
the self argument contains the instance.
In the case of class SnailRun, we'll want
to display the snail's name (.name
attribute) and finishing time (.time
attribute). Here's one possible version:
def __str__ ( self ):
"""Return a string representation of self.
"""
return "{0:8.1f} {1}".format(self.time, self.name)
This method will format the finishing time into an 8-character string, with one digit after the decimal point, followed by one space, then the snail's name.
Let's assume that the __cmp__() and __str__() methods have been added to our snails.py module, and show their use in some
conversational examples.
>>> from snails import *
>>> sally4 = SnailRun('Sally', 88.8)
>>> jim4=SnailRun('Jim', 76.5)
>>>
Now that we have two SnailRun instances,
we can show how the __str__() method
formats them for printing:
>>> print sally4
88.8 Sally
>>> print jim4
76.5 Jim
>>>
We can also show the various ways that Python compares
two instances using our new __cmp__() method.
>>> cmp(sally4,jim4) 1 >>> sally4 > jim4 True >>> sally4 <= jim4 False >>> sally4 < jim4 False >>>
Now that we have defined how instances are to be ordered, we can sort a list of them in order by finish time. First we throw them into the list in any old order:
>>> judy4 = SnailRun ( 'Judy', 67.3 )
>>> blake4 = SnailRun ( 'Blake', 181.4 )
>>> race4 = [sally4, jim4, judy4, blake4]
>>> for run in race4:
... print run
...
88.8 Sally
76.5 Jim
67.3 Judy
181.4 Blake
>>>
The .sort() method on a list uses Python's
cmp() function to compare items when
sorting them, and this in turn will call our class's
__cmp__() method to sort them by finishing
time.
>>> race4.sort()
>>> for run in race4:
... print run
...
67.3 Judy
76.5 Jim
88.8 Sally
181.4 Blake
>>>
For an extended example of a class that implements a number of
special methods, see rational.py: An example Python
class. This example shows how to define
a new kind of numbers, and specify how operators such as
“+” and “*” operate on instances.