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 Pets. 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?? +++

results matching ""

    No results matching ""