Inheritance vs. delegation in Python

Object-oriented programming in Python part 4: using delegation rather than inheritance in Python

In previous episodes of my tiny introduction to object-oriented programming in Python, we've had a look at what objects and classes are and at inheritance. Then we took a brief detour to look at closures, which are like objects in some ways. Today let's have a look at delegation.

We've previously seen that inheritance is a useful way of specializing or otherwise changing the behavior of a class that already exists. Another way of looking at that is that your class is getting some other class to do most of the work.

There's another way of getting another class to do most of the work that's sometimes useful. It involves explicitly handing particular method calls off to an instance of another class. That's such an obvious technique that it seems that it hardly deserves a name, but it's called delegation.

Let's say we wanted a dictionary that didn't raise a KeyError when a key wasn't found, but instead returned a default value of None. (You don't need to implement that since it's in the collections module as of Python 2.4 but it's a useful example.)

One way to implement that using inheritance is like this:

class defaultDict(dict):

  def __getitem__(self,key):
    if self.has_key(key):
      return self.get(key)
      return None # Default value

If, for some reason, you didn't want to use inheritance, you could give your defaultDict instances dicts of their own and call those dicts' methods when the same methods of your instances were called, making whatever changes you wanted. Here's one way to implement that:

class defaultDict:
  def __init__(self):

  def __setitem__(self,key,value):

  def __getitem__(self,key):
    if self.d.has_key(key):
      return self.d[key]
      return None # Default

(Here's a bit of terminology: Delegation is also sometimes called "containment". The inheritance relationship is sometimes calls "is-a" or "ISA", as in "the defaultDict is a dict". The delegation relationship is sometimes called "has-a" or "HASA", as in "the defaultDict has a dict".)

You might reasonably ask why anyone would want to do that. It's more code (a lot more if you wanted all of a dict's methods to work). One reason you might have done it once upon a time is that you used not to be able to inherit from built-in types. But that has since been fixed in Python, though the UserDict and UserList modules that did the delegation so that you could inherit from them are still in the standard library.

The real reason that you'd want to use delegation is that the class you were designing has some resemblance to another class, but isn't really enough like it that you'd call it the same kind of thing. That's obviously not the case with our defaultDict. But take, for example, the Message class in Python's email module. You can index a Message object as though it were a dictionary to get at the message's headers, such as "Subject" and "From". Even if you didn't index an email message, it might well make sense to store the headers in something like dictionary. But I don't think that anyone I know would call an email message a kind of dictionary. (The Message class doesn't actually use a dictionary, but that's an implementation detail that's not important for our purposes here.)

One reasonable way to choose between inheritance and delegation is to look at whether you want all of the other class's methods. While it makes sense to get and set an email message's headers, a dictionary's pop() method probably doesn't make sense for an email message and neither, really, do a do a few others. Similarly, a dict's len() method is obvious enough, but finding the length of an email message isn't really related to the number of keys in a dictionary.

So if inheriting would mean that you would need to turn off some methods or implement some in a way that's not related to the parent class's implementation, you may be better off with delegation.

Posted: Sat - November 11, 2006 at 07:43 PM   Main   Category: