Parent, child class, methods override Polymorphism
Chapter 12 Inheritance
In Object Oriented Programming(OOP), a great benefit is to reuse code. Inheritance is one way to achieve this.
Inheritance enables us to make new class based on an existing class. The new class(child class or subclass) will inherit all the attributes of the existing class(parent class or superclass), and on top of that, a child class can also override attributes from the parent class or have extra features(?methods? fields?) of its own.
12.1 Parent and Child Class
animals,farm animal, cow, chicken, (name, color, type, legNumber, home)
Syntax Child class is declared as usual class, only has a parentheses after it's name with Parent class name inside:
class Parent:
code...
class Child(Parent):
code...
Given that Child class inherits from the Parent class, we can say the following:
- Child is derived from Parent
- Child extends Parent
- Parent is a superclass of Child; Child is a subclass of Parent
- Child isa Parent (Wait to see examples to understand this)
Let's see an example of a parent class FarmAnimal
, a child class Duck
to show how inheritance works:
class FarmAnimal:
def __init__(self, type='Animal', legNumber=0):
self.type = type
self.legNumber = legNumber
def setLeg(self, legNumber):
self.legNumber = legNumber
def getLeg(self):
return self.legNumber
def describe(self):
print( "%s has %d legs, it lives on the farm." %(self.type, self.legNumber) )
class Duck(FarmAnimal):
def makeSound(self):
print("Quark! Quark! Quark!")
In class FarmAnimal
, we defined a constructor (two instance variables type
and legNumber
), a setter and a getter methods (setLeg
and getLeg
) and another method ( describe()
) to show information about the object.
Instead of starting from scratch, another class Duck
is derived from a preexisting class FarmAnimal
by putting the parent class name in parentheses after the new class name ( Duck(FarmAnimal)
) in the header line.
Duck
is a child class of FarmAnimal
, it will inherent all the attributes of it's parent class FarmAnimal
. Let's see:
Continue with previous codes
duck = Duck() # create a Duck instance
print( duck.legNumber ) # 0 (variable defined in parent class)
duck.setLeg(2) # access parent method
print( duck.legNumber ) # 2
duck2 = Duck('Duck', 2) # another Duckl instance ( access the constructor in parent class)
duck2.describe() # Duck has 2 legs, it lives on the farm. (access parent class method)
duck2.makeSound() # Quark! Quark! Quark! (access method in child class)
From the above application of parent, child classes, we can see that the child class Duck
inherit all the attributes defined in the parent class, it has the constructor, the setLeg()
and getLeg()
and describe()
methods; it also has it's own method makeSound()
.
The child class inherits the attributes of its parent class, and you can use those attributes as if they were defined in the child class.
Overriding methods
A child class can also override data members and methods from the parent.
Let's continue with the example in previous lesson, make another child class of the parent class FarmAnimal
, name it Cow
# continue with `FarmAnimal` class
class Cow(FarmAnimal):
def __init__(self, legNumber = 4)
self.type = 'Cow'
self.legNumber = legNumber
def makeSound(self):
print("Moo! Moo! Moo!")
def getLeg(self):
if self.legNumber != 4:
return "Holy Cow! A cow with %d legs!" % (self.legNumber)
else:
return self.legNumber
# Use the Cow class
cow = Cow() # create an instance of Cow (invoke the constructor in Cow, it override the parent class constructor)
print(cow.legNumber) # 4 (child class constructor sets the default value)
cow.describe() # Cow has 4 legs, it lives on the farm.
cow.setLeg(2) # access setter method defined in the parent class
print( cow.getLeg() ) # Holy Cow! A cow with 2 legs!
cow2 = Cow(3) # create another instance (again invoke the child class constructor)
cow2.describe() # Cow has 3 legs, it lives on the farm.
cow2.makeSound() # Moo! Moo! Moo! (method of it's own)
From the above example, you can see, the constructor in child class Cow
override the constructor in parent class FarmAnimal
, so when creating Cow
instances, cow
and cow2
get their initial state values from the child constructor;
Cow
also overrides method getLet()
, which prints out a message when letNumber
is not 4.
Cow
inherits all other methods from the parent class, setLeg()
, describe()
;
Cow
defines it's own method makeSound()
to print out message of a cow's sound "Moo! Moo! Moo!"
Pet and Cat classes
See another inheritance example for better understanding: Pet
and Cat
classes.
Pet
is the parent class, it has 4 fields: name, color, type
, and legNumber
. Cat
is a child class that derived from Pet
class; Kitten
is a child class derived from Cat
. So Cat
and Kitten
are all Pet
s. They all inherit the 4 fields from Pet
, but they both have some new feature of their own.
Ex. 12.1-1 PetCat.py
The file named PetCat.py
(module) contains two classes: Pet
and Cat
# PetCat.py
class Pet:
def __init__ (self, n=' ', c=' ', t=' ', a=0):
self.name = n
self.color = c
self.type = t
self.age = a
def setName (self, n ):
self.name = n
def getName (self):
return self.name
def setColor (self, c ):
self.color = c
def getColor (self):
return self.age
def setType (self, t ):
self.type = t
def getType (self):
return self.type
def setAge (self, a)
self.age = a
def getAge (self):
return "{0} years".format(self.age)
def __str__ (self):
return "{0} is a {1} {2} aged {3} years.".format (self.name, self.color, self.type, self.age)
class Cat( Pet ): # Cat is a child class, Pet is the parent class
def __init__ (self, n=' ', c=' ', a=0):
self.name = n
self.color = c
self.type = 'cat'
self.age = a
def setType (self, t)
print ('{0} refuses to change type, it will always be a {1} in this life.'. format (self.name, self.type) )
def setAge(self, a):
if 1<= a <30:
self.age = a
Pet
class has four things common to all pets: name, color, type
and age
. The code also has setters and getters (accessors). At the end the __str__( )
method can print a simple message about this pet’s name
, age
, type
and color
.
Cat
is subclass of Pet
.
Cat
class constructor accepts only name, color
and age
, it’s type
is fixed to be 'cat'
. The new constructor overrides the Pet
constructor.
Cat
also overrides the setter setType( )
. If someone tries to change the Cat
type, he will not be able to change it, instead he will get a message “…refuses to change type, it will always be a cat in this life!”
.
'Cat' also overrides setAge()
method, it puts a limit on the age to be 1<age<30
(in years).
Let's use Pet
and Cat
in an application:
Ex. 12.1-2 test_PetCat.py
Pet
and Cat
classes are all saved in one file named PetCat.py
, to use them we need to import PetCat
module.
# test PetCat
import PetCat # or use: from PetCat import Pet, Cat
c = Cat('Sassy', 'white', 3)>
print(c) # Sassy is a white cat aged 3. (invokes the `__str__()` inherited from parent class `Pet`)
c.setAge(0.5) # not in the range defined in the override `setAge()` method
c.getAge() # 3 years. (age not changed means the override setter is working, also the inherited getAge() is working)
c.setType('dog') # Sassy refuses to change type, it will always be a cat in this life. (the override setter is in action)
c.setName('Pussy') # (inherited setter from Pet class)
print (c) # Pussy is a white cat aged 3.
Next, let's make a subclass of Cat
named Kitten
, see if Kitten
inherits anything from Pet
. (What's your guess?)
Ex. 12.1-3 Kitten.py
# Kitten.py subclass of Cat
from PetCat import Pet, Cat
class Kitten( Cat ):
"""Kitten is subclass of Cat, which is a subclass of Pet.
Kitten's age is usually from 0 to 12 months."""
def setAge(self, a):
if 0<a<12:
self.age = a
def getAge(self)
return ' {0} months'.format(self.age)
def __str__(self):
return ( '{0} is a {1} months old {2} Kitten.'.format(self.name, self.age, self.color) )
def say(self ):
print( '{0} says Meow, Meow, Meow! Milk please!'.format (self.name) )
k = Kitten( 'Tussy', 'yellow', 10 )
print (k) # Tussy is a 10 months old yellow kitten.
k.setAge(8)
print ( k.getAge() ) # 8 months
k.say() # Tussy says Meow, Meow, Meow! Milk please!
Kitten
is subclass of Cat
, so it inherits Cat
and Pet
.
Kitten
class inherits Cat
constructor. But it overrides the setter setAge()
, it puts a limit on age to be 0<age<12
(in months). And Kitten
overrides the getter getAge()
in Pet
so to return a corresponding message , age in months
.
Kitten
also overrides the__str__()
method (which is defined in Pet
) for a fitted display of it's objects.
To show that Kitten
inherits Pet
, we can try setName()
(which is also defined in Pet
) see if it works on Kitten
object.
'Kitten' has a new method say()
, if you call k.say()
, you will get like 'Tussy says Meow, Meow, Meow! Milk please! ' printed.
==end
++++ ??? if I write Kitten in a new file, I use import PetCat, sth goes wrong say Cat is not defined. if I use from PetCat import *, everything is fine
it looks like import PetCat lost Cat?? or what?? +++