Class Inheritance Victor Norman CS104

advertisement
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.
Download