def search(obj): if isinstance(obj, Mailbox): ... # code to search a mailbox elif isinstance(obj, Document): ... # code to search a document elif ... 

A better approach is to define a search() method on all the classes and just call it: 

classMailbox : def search(self): ... # code to search a mailbox classDocument : def search(self): ... # code to search a document obj.search() 

## 2.6.5 What is delegation? 

Delegation is an object oriented technique (also called a design pattern). Let’s say you have an object$_{x}$ and want to change the behaviour of just one of its methods. You can create a new class that provides a new implementation of the method you’re interested in changing and delegates all other methods to the corresponding method of$_{x}$. 

Python programmers can easily implement delegation. For example, the following class implements a class that behaves like a file but converts all written data to uppercase: 

classUpperOut : def __init__ (self, outfile): self._outfile=outfile def write(self, s): self._outfile.write(s.upper()) def __getattr__ (self, name): return getattr(self._outfile, name) 

Here the$_{UpperOut}$class redefines the$_{write()}$ method to convert the argument string to uppercase before calling the underlying$_{self._outfile.write()}$ method. All other methods are delegated to the underlying$_{self.}$ _outfile object. The delegation is accomplished via the$_{__getattr__}$ method; consult the language reference for more information about controlling attribute access. 

Note that for more general cases delegation can get trickier. When attributes must be set as well as retrieved, the class must define a __setattr__() method too, and it must do so carefully. The basic implementation of$_{__setattr__()}$ is roughly equivalent to the following: 

classX : ... def __setattr__ (self, name, value): self.__dict__ [name] =value ... 

Most$_{__setattr__()}$ implementations must modify$_{self.__dict__}$ to store local state for self without caus- ing an infinite recursion. 

