[ Team LiB ] |
22.3 OOP and Inheritance: "is-a" RelationshipsWe've talked about the mechanics of inheritance in depth already, but we'd like to show you an example of how it can be used to model real-world relationships. From a programmer's point of view, inheritance is kicked off by attribute qualifications, and triggers a search for a name in an instance, its class, and then its superclasses. From a designer's point of view, inheritance is a way to specify set membership: a class defines a set of properties that may be inherited and customized by more specific sets (i.e., subclasses). To illustrate, let's put that pizza-making robot we talked about at the start of this part of the book to work. Suppose we've decided to explore alternative career paths and open a pizza restaurant. One of the first things we'll need to do is hire employees to serve customers, make the pizza, and so on. Being engineers at heart, we've also decided to build a robot to make the pizzas; but being politically and cybernetically correct, we've also decided to make our robot a full-fledged employee, with a salary. Our pizza shop team can be defined by the following classes in the example file employees.py. It defines four classes and some self-test code. The most general class, Employee, provides common behavior such as bumping up salaries (giveRaise) and printing (_ _repr__). There are two kinds of employees, and so two subclasses of Employee—Chef and Server. Both override the inherited work method to print more specific messages. Finally, our pizza robot is modeled by an even more specific class: PizzaRobot is a kind of Chef, which is a kind of Employee. In OOP terms, we call these relationships "is-a" links: a robot is a chef, which is a(n) employee. class Employee: def __init__(self, name, salary=0): self.name = name self.salary = salary def giveRaise(self, percent): self.salary = self.salary + (self.salary * percent) def work(self): print self.name, "does stuff" def __repr__(self): return "<Employee: name=%s, salary=%s>" % (self.name, self.salary) class Chef(Employee): def __init__(self, name): Employee.__init__(self, name, 50000) def work(self): print self.name, "makes food" class Server(Employee): def __init__(self, name): Employee.__init__(self, name, 40000) def work(self): print self.name, "interfaces with customer" class PizzaRobot(Chef): def __init__(self, name): Chef.__init__(self, name) def work(self): print self.name, "makes pizza" if __name__ == "__main__": bob = PizzaRobot('bob') # Make a robot named bob. print bob # Runs inherited __repr__ bob.work( ) # Run type-specific action. bob.giveRaise(0.20) # Give bob a 20% raise. print bob; print for klass in Employee, Chef, Server, PizzaRobot: obj = klass(klass.__name__) obj.work( ) When we run this module's self-test code, we create a pizza-making robot named bob, which inherits names from three classes: PizzaRobot, Chef, and Employee. For instance, printing bob runs the Employee.__repr__ method, and giving bob a raise invokes Employee.giveRaise, because that's where inheritance finds it. C:\python\examples> python employees.py <Employee: name=bob, salary=50000> bob makes pizza <Employee: name=bob, salary=60000.0> Employee does stuff Chef makes food Server interfaces with customer PizzaRobot makes pizza In a class hierarchy like this, you can usually make instances of any of the classes, not just the ones at the bottom. For instance, the for loop in this module's self-test code creates instances of all four classes; each responds differently when asked to work, because the work method is different in each. Really, these classes just simulate real world objects; work prints a message for the time being, but could be expanded to really work later. |
[ Team LiB ] |