6.6 Operator Overloading in Classes

6.6 Operator Overloading in Classes

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 

6.6.1 Common Operator Overloading Methods

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

Method

Overloads

Called for

__ init __

Constructor

Object creation: Class()

__ del __

Destructor

Object reclamation

_ _ add __

Operator '+'

 X + Y 

__ or __

Operator '' (bitwise or)

 X  Y 

__ repr __

Printing, conversions

print X, `X`

__ call __

Function calls

 X() 

__ getattr __

Qualification

 X.undefined 

__ getitem __

Indexing

X[key] , for loops , in tests

__ setitem __

Index assignment

 X[key] = value 

__ getslice __

Slicing

 X[low:high] 

__ len __

Length

 len(X), truth tests 

__ cmp __

Comparison

 X == Y, X < Y 

__ radd __

Right-side operator '+'

Noninstance + X

6.6.2 Examples

Let's illustrate a few of the methods in Table 6.1 by example.

6.6.2.1 __getitem__ intercepts all index references

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 
6.6.2.2 __getattr__ catches undefined attribute references

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.

6.6.2.3 _ _repr__ returns a string representation

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.



Learning Python
Learning Python: Powerful Object-Oriented Programming
ISBN: 0596158068
EAN: 2147483647
Year: 1999
Pages: 156
Authors: Mark Lutz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net