15.04.2013 Views

Core Python Programming (2nd Edition)

Core Python Programming (2nd Edition)

Core Python Programming (2nd Edition)

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Delegation is a characteristic of wrapping that simplifies the process with regard to dictating<br />

functionality by taking advantage of pre-existing functionality to maximize code reuse.<br />

Wrapping a type generally consists of some sort of customization to the existing type. As we mentioned<br />

before, this tweaking comes in the form of new, modified, or removed functionality compared to the<br />

original product. Everything else should remain the same, or keep its existing functionality and behavior.<br />

Delegation is the process whereby all the updated functionality is handled as part of the new class, but<br />

the existing functionality is delegated to the default attributes of the object.<br />

The key to implementing delegation is to override the __getattr__() method with code containing a call<br />

to the built-in getattr() function. Specifically, getattr() is invoked to obtain the default object attribute<br />

(data attribute or method) and return it for access or invocation. The way the special method<br />

__getattr__() works is that when an attribute is searched for, any local ones are found first (the<br />

customized ones). If the search fails, then __getattr__() is invoked, which then calls getattr() to obtain<br />

an object's default behavior.<br />

In other words, when an attribute is referenced, the <strong>Python</strong> interpreter will attempt to find that name in<br />

the local namespace, such as a customized method or local instance attribute. If it is not found in the<br />

local dictionary, then the class namespace is searched, just in case a class attribute was accessed.<br />

Finally, if both searches fail, the hunt begins to delegate the request to the original object, and that is<br />

when __getattr__() is invoked.<br />

Simple Example Wrapping Any Object<br />

Let us take a look at an example. Here we present a class that wraps nearly any object, providing such<br />

basic functionality as string representations with repr() and str(). Additional customization comes in<br />

the form of the get() method, which removes the wrapping and returns the raw object. All remaining<br />

functionality is delegated to the object's native attributes as retrieved by __getattr__() when necessary.<br />

Here is an example of a wrapping class:<br />

class WrapMe(object):<br />

def __init__(self, obj):<br />

self.__data = obj<br />

def get(self):<br />

return self.__data<br />

def __repr__(self):<br />

return 'self.__data'<br />

def __str__(self):<br />

return str(self.__data)<br />

def __getattr__(self, attr):<br />

return getattr(self.__data, attr)<br />

In our first example, we will use complex numbers, because of all <strong>Python</strong>'s numeric types, complex<br />

numbers are the only one with attributes: data attributes as well as its conjugate() built-in method.<br />

Remember that attributes can be both data attributes as well as functions or methods:<br />

>>> wrappedComplex = WrapMe(3.5+4.2j)<br />

>>> wrappedComplex # wrapped object: repr()<br />

(3.5+4.2j)<br />

>>> wrappedComplex.real # real attribute<br />

3.5

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!