Class Inheritance Victor Norman CS104 The Problem What is the basic problem that inheritance tries to solve? Answer: Two types (classes) may have lots of similar code. Inheritance is a way to reuse code, thus removing duplicate code. Drawing Man and Woman class Man: def __init__(self, x, y): self._x = x self._y = y def draw(self): self.drawHead() self.drawBody() self.drawArms() self.drawLegs() def drawHead(self): <code here> # code for drawArms # code for drawBody def drawLegs(self): <code to draw legs> class Woman: def __init__(self, x, y): self._x = x self._y = y def draw(self): self.drawHead() self.drawBody() self.drawArms() self.drawLegs() def drawHead(self): <code here> # code for drawArms # code for drawBody def drawLegs(self): <code to draw skirt> Lots of duplicate code • So what? Who cares? • Duplicate code – more places to make errors – more places to fix errors – inconsistent fixes – more typing – more places to add new code Solution: a Person class instead of Man/Woman class Person: class Man: def __init__(self, x, y, gen): def __init__(self, x, y): self._x = x self._x = x self._y = y self._y = y self._gender = gen def draw(self): def draw(self): self.drawHead() self.drawHead() self.drawBody() self.drawBody() self.drawArms() self.drawArms() self.drawLegs() self.drawLegs() # drawHead, Body, Arms code… # drawHead, Body, Arms code… def drawLegs(self): if self._gender == "F": def drawLegs(self): self.drawSkirtAndLegs() <code to draw legs> else: self.drawHairyLegs() def drawHairyLegs(self): <code> def drawSkirtAndLegs(self): <code> Evaluate this solution + Much less code in Person than in Man + Woman. + Only 1 class. (Less is almost always better.) − Does not scale well: Consider: adding Alien now: 3 legs, that bend backwards. def drawLegs(self): if self._gender == “F”: self.drawSkirtAndLegs() elif self._gender == “M”: self.drawHairyLegs() elif self._gender == “A”: self.drawAlienLegs() If we decide to drawHeads differently we need a big if-elif-elif again… and same for drawBody()… Solution: class inheritance • Keep Person, and make Man, Woman, and Alien be subclasses Person Man Woman Alien Terminology • Person is the superclass, or base class, or parent class. • Man, Woman, Alien are subclasses, derived classes, or child classes. • Subclasses inherit from or are derived from superclasses. • Want all common attributes and methods/code “pushed” up the hierarchy. – e.g., Man, Woman, Alien all have x, y location: should be stored in Person superclass. Person Class Code class Person: # an abstract base class. def __init__(self, x_loc, y_loc, gender): self._x = x_loc self._y = y_loc self._gender = gender def draw(self): self.drawHead() self.drawBody() self.drawArms() self.drawLegs() # def drawHead, drawBody, drawArms, etc. # NOTE: No drawLegs() code here. Only in # derived classes. Also, don’t really need # to store gender anymore. Man class Man(Person): “A Man is-a Person” def __init__(self, x_loc, y_loc): Person.__init__(self, x_loc, y_loc, "M") def drawLegs(self): # code to draw hairy bowlegged legs wearing jeans. Call superclass constructor, passing in reference to self, location and gender drawLegs() implemented here – not in superclass Woman, Alien from person import * # assuming Person is in person.py class Woman(Person): def __init__(self, x_loc, y_loc): Person.__init__(self, x_loc, y_loc, “F”) def drawLegs(self): # code to draw legs under a nice modest skirt. class Alien(Person): def __init__(self, x_loc, y_loc): Person.__init__(self, x_loc, y_loc, "A") def drawLegs(self): # code to draw 3 legs. Using this code barack = Man(10, 20) beyonce = Woman(30, 40) e_t = Alien(50, 60) beings = [ barack, beyonce, e_t ] for be in beings: be.draw() myro.py details • myro library provides move(), motors(), turnLeft(), turnRight(), setLEDs(), beep(), etc. – When you call move(), you are actually calling • robot.move(): robot is a pre-defined global object. • Also, provides a class definition: class Scribbler: def __init__(self): … def init(self, comport=None): … def turnLeft(self, time): … def beep(self, time, freq1, freq2=None): … What we did last week • We defined ScribPoint – attributes: _x, _y – getX(), getY(), setX(), setY() – Just held the coordinates. Not much fcnality. • We defined goToPoint(from, to): – from and to were ScribPoint objects. – used our turnDegrees(), which called turnLeftDegrees() or turnRightDegrees(), which used globally pre-defined robot object. Analysis of this solution • ScribPoint has very little functionality. • We had to make the robot turn east after moving to a new point. – More turns more inconsistent results. • Had to pass in to goToPoint() where the robot was. Better way • Wouldn’t it be loverly if – the robot could “remember” where it is and what angle it is pointing at. • But, how to do this? Two options: – Have a class that “has-a” a reference to a Scribbler object and keeps track of this stuff, or, – Have a class that “is-a” Scribbler object, adding the functionality and attributes. • Latter is Class Inheritance. CS104Scribbler • A CS104Scribbler “is-a” Scribbler. – inherits from it. from myro import * # imports Scribbler defn. class CS104Scribbler(Scribbler): def __init__(self, comport=“COM40”, x=0, y=0, angle=0): Scribbler.__init__(self, comport) # code to store x, y, angle. Result • We can move goToPoint(), turnDegrees(), etc., into CS104Scribbler class. • Can write this nice code: scrib = CS104Scribbler() scrib.goToPoint(16, 12) scrib.goToPoint(0, 0) # uses COM40 • Nice and compact and readable. • goToPoint() does not have to “turn east” after each move, because object can remember what angle it is pointing. Additional Benefits • We could write code to control 2 robots: scrib1 = CS104Scribbler(“COM40”) scrib2 = CS104Scribbler(“COM41”) # send different messages to robots scrib1.beep(3, 660, 770) scrib2.beep(3, 440, 487) Extra Slides… New class or child class? Q: If a class needs a couple attributes belonging to another class, is it better to make a child class or a completely new class? A: If it is just attributes, just make a new class. If it is attributes and a bunch of functionality, make a subclass. When to subclass? Q: When would it be better to use the Parent and Child class rather than defining methods in one class? A: 1) You might need to be able to make objects of both kinds of classes. 2) You might have the Parent class well tested – so you don’t want to alter it. 3) You anticipate that you might need another child class of the parent. How to see defined methods? Q: Is there a way to see all the preexisting methods for a parent class? A: Best way is to do >>> help(class) in interactive mode.