Online Book Reader

Home Category

Learning Python - Mark Lutz [364]

By Root 1819 0
pay):

self.person = Person(name, 'mgr', pay) # Embed a Person object

def giveRaise(self, percent, bonus=.10):

self.person.giveRaise(percent + bonus) # Intercept and delegate

def __getattr__(self, attr):

return getattr(self.person, attr) # Delegate all other attrs

def __str__(self):

return str(self.person) # Must overload again (in 3.0)

if __name__ == '__main__':

...same...

In fact, this Manager alternative is representative of a general coding pattern usually known as delegation—a composite-based structure that manages a wrapped object and propagates method calls to it. This pattern works in our example, but it requires about twice as much code and is less well suited than inheritance to the kinds of direct customizations we meant to express (in fact, no reasonable Python programmer would code this example this way in practice, except perhaps those writing general tutorials). Manager isn’t really a Person here, so we need extra code to manually dispatch method calls to the embedded object; operator overloading methods like __str__ must be redefined (in 3.0, at least, as noted in the upcoming sidebar Catching Built-in Attributes in 3.0), and adding new Manager behavior is less straightforward since state information is one level removed.

Still, object embedding, and design patterns based upon it, can be a very good fit when embedded objects require more limited interaction with the container than direct customization implies. A controller layer like this alternative Manager, for example, might come in handy if we want to trace or validate calls to another object’s methods (indeed, we will use a nearly identical coding pattern when we study class decorators later in the book). Moreover, a hypothetical Department class like the following could aggregate other objects in order to treat them as a set. Add this to the bottom of the person.py file to try this on your own:

# Aggregate embedded objects into a composite

...

bob = Person(...)

sue = Person(...)

tom = Manager(...)

class Department:

def __init__(self, *args):

self.members = list(args)

def addMember(self, person):

self.members.append(person)

def giveRaises(self, percent):

for person in self.members:

person.giveRaise(percent)

def showAll(self):

for person in self.members:

print(person)

development = Department(bob, sue) # Embed objects in a composite

development.addMember(tom)

development.giveRaises(.10) # Runs embedded objects' giveRaise

development.showAll() # Runs embedded objects' __str__s

Interestingly, this code uses both inheritance and composition—Department is a composite that embeds and controls other objects to aggregate, but the embedded Person and Manager objects themselves use inheritance to customize. As another example, a GUI might similarly use inheritance to customize the behavior or appearance of labels and buttons, but also composition to build up larger packages of embedded widgets, such as input forms, calculators, and text editors. The class structure to use depends on the objects you are trying to model.

Design issues like composition are explored in Chapter 30, so we’ll postpone further investigations for now. But again, in terms of the basic mechanics of OOP in Python, our Person and Manager classes already tell the entire story. Having mastered the basics of OOP, though, developing general tools for applying it more easily in your scripts is often a natural next step—and the topic of the next section.

* * *

Catching Built-in Attributes in 3.0


In Python 3.0 (and 2.6 if new-style classes are used), the alternative delegation-based Manager class we just coded will not be able to intercept and delegate operator overloading method attributes like __str__ without redefining them. Although we know that __str__ is the only such name used in our specific example, this a general issue for delegation-based classes.

Recall that built-in operations like printing and indexing implicitly invoke operator overloading methods such as __str__ and __getitem__. In 3.0, built-in operations like these do not route

Return Main Page Previous Page Next Page

®Online Book Reader