Inheritance and Polymorphism

Please re-open notebook rsepython-s4r7.ipynb

Object-based vs Object-Oriented

So far we have seen only object-based programming, not object-oriented programming.

Using Objects doesn’t mean your code is object-oriented.

To understand object-oriented programming, we need to introduce polymorphism and inheritance.

Inheritance

Ontology and inheritance

Inheritance in python

class Animal(object):
    def beBorn(self): print("I exist")
    def die(self): print("Argh!")

class Bird(Animal):
    def fly(self): print("Whee!")

class Eagle(Bird):
    def hunt(self): print("I'm gonna eat cha!")

Eagle().beBorn()
Eagle().hunt()

I exist

I’m gonna eat cha!

Inheritance terminology

(These are different terms for the same thing.)

Another equivalent definition is using the synonym child / parent for derived / base class:

Inheritance and constructors

To use implicitly constructors from a superclass, we can use super as shown below.

class Animal(object):
    def __init__(self, age):
        self.age=age

class Person(Animal):
    def __init__(self, age, name):
        super(Person, self).__init__(age)
        self.name=name

Read Raymond Hettinger’s article about [super](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/) to see various real examples.

Inheritance UML diagrams

UML shows inheritance with an open triangular arrow pointing from subclass to superclass.

yuml("[Animal]^-[Bird],[Bird]^-[Eagle],[Bird]^-[Starling]%")

YUML model - inheritance

Aggregation vs Inheritance

If one object has or owns one or more objects, this is not inheritance.

For example, in my solution to the Boids task, the overall Model owned several Boids, and each Boid owned two 2-vectors, one for position and one for velocity.

Aggregation in UML

The Boids situation can be represented thus:

yuml("[Model]<>-*>[Boid],[Boid]position++->[Vector],[Boid]velocity++->[Vector]%")

YUML model - boids

The open diamond indicates Aggregation, the closed diamond composition.

(A given boid might belong to multiple models, a given position vector is forever part of the corresponding Boid.)

The asterisk represents cardinality, a model may contain multiple Boids. This is a [one to many relationship](https://en.wikipedia.org/wiki/One-to-many_(data_model). A [many to many relationship](https://en.wikipedia.org/wiki/Many-to-many_(data_model) is shown with * on both sides.

Refactoring to inheritance

Smell: Repeated code between two classes which are both ontologically subtypes of something

Before:

class Person:
    def __init__(self, age, job):
        self.age = age
        self.job = job
    def birthday(self):
        self.age += 1

class Pet:
    def __init__(self, age, owner):
        self.age = age
        self.owner = owner
    def birthday(self):
        self.age += 1

After:

class Animal:
    def __init__(self, age):
        self.age = age
    def birthday(self):
        self.age += 1

class Person(Animal):
    def __init__(self, age, job):
        self.job = job
        super(Person, self).__init__(age)

class Pet(Animal):
    def __init__(self, age, owner):
        self.owner = owner
        super().__init__(age)

Polymorphism

Consider the following:

class Dog(object):
    def noise(self):
        return "Bark"

class Cat(object):
    def noise(self):
        return "Miaow"

class Pig(object):
    def noise(self): return "Oink"

class Cow(object):
    def noise(self): return "Moo"

animals=[Dog(), Dog(), Cat(), Pig(), Cow(), Cat()]
for animal in animals:
    print(animal.noise())

Bark

Bark

Miaow

Oink

Moo

Miaow

This will print “Bark Bark Miaow Oink Moo Miaow”

If two classes support the same method, but it does different things for the two classes, then if an object is of an unknown class, calling the method will invoke the version for whatever class the instance is an instance of.

Polymorphism and Inheritance

Often, polymorphism uses multiple derived classes with a common base class.

However, duck typing in Python means that all that is required is that the types support a common Concept (Such as iterable, or container, or, in this case, the Noisy concept.)

A common base class is used where there is a likely default that you want several of the derived classes to have.

class Animal(object):
    def noise(self): return "I don't make a noise."

class Dog(Animal):
    def noise(self): return "Bark"

class Worm(Animal):
    pass

class Poodle(Dog):
    pass

animals=[Dog(), Worm(), Pig(), Cow(), Poodle()]
for animal in animals:
    print(animal.noise())

Bark

I don’t make a noise.

Oink

Moo

Bark

Undefined Functions and Polymorphism

In the above example, we put in a dummy noise for Animals that don’t know what type they are.

Instead, we can explicitly deliberately leave this undefined, and we get a crash if we access an undefined method.

class Animal(object): pass

class Worm(Animal): pass
Worm().noise() # Generates error

—————————————————————————

AttributeError Traceback (most recent call last)

in () </span>

—-> 1 Worm().noise() # Generates error

AttributeError: ‘Worm’ object has no attribute ‘noise’

Refactoring to Polymorphism

Smell: a function uses a big set of if statements or a case statement to decide what to do:

Before:

class Animal(object):
    def __init__(self,animal_kind):
        self.animal_kind=animal_kind

    def noise(self):
        if self.animal_kind=="Dog":
            return "Bark"
        elif self.animal_kind=="Cat":
            return "Miaow"
        elif self.animal_kind=="Cow":
            return "Moo"

which is better replaced by the code above.

Interfaces and concepts

In C++, it is common to define classes which declare dummy methods, called “virtual” methods, which specify the methods which derived classes must implement. Classes which define these methods, which cannot be instantiated into actual objects, are called “abstract base” classes or “interfaces”.

Python’s Duck Typing approach means explicitly declaring these is unnecessary: any class concept which implements appropriately named methods will do. These as user-defined concepts, just as “iterable” or “container” are built-in Python concepts. A class is said to “implement and interface” or “satisfy a concept”.

Interfaces in UML

Interfaces implementation in UML is indicated thus:

yuml("[<<Animal>>]^-.-[Dog]")

YUML model - interfaces

Further UML

UML is a much larger diagram language than the aspects we’ve shown here.

Read more about UML in Martin Fowler’s book.

Next: Reading - Patterns