16.22. OOP Inheritance Patterns

  • No Inheritance

  • Single Inheritance

  • Multilevel Inheritance

  • Multiple Inheritance (with mixin classes)

single inheritance

One class inherits from one other class. Has one parent.

multilevel inheritance

One class inherits from other class, and yet another class inherits from it. This creates hierarchical structure.

multiple inheritance
mixin classes

One class derives from several other classes at once.

16.22.1. Starting Point

>>> class Car:
...     pass
...
>>> class Truck:
...     pass
...
...
>>> maluch = Car()
>>> scania = Truck()

16.22.2. No Inheritance

>>> class Car:
...     def engine_start(self): ...
...     def engine_stop(self): ...
...
>>> class Truck:
...     def engine_start(self): ...
...     def engine_stop(self): ...
...
...
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()

16.22.3. Single Inheritance

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
...
>>> class Car(Vehicle):
...     pass
...
>>> class Truck(Vehicle):
...     pass
...
...
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()

Next:

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
...     def window_open(self): ...
...     def window_close(self): ...
...
>>> class Car(Vehicle):
...     pass
...
>>> class Truck(Vehicle):
...     pass
...
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()

Problem:

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
...     def window_open(self): ...
...     def window_close(self): ...
...
>>> class Car(Vehicle):
...     pass
...
>>> class Truck(Vehicle):
...     pass
...
>>> class Motorbike(Vehicle):
...     pass
...
...
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()
>>>
>>> yamaha = Motorbike()
>>> yamaha.engine_start()
>>> yamaha.engine_stop()
>>> yamaha.window_open()  # motorbikes don't have windows
>>> yamaha.window_close()  # motorbikes don't have windows

Solution 1 - code duplication:

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
>>>
>>> class Car(Vehicle):
...     def window_open(self): ...
...     def window_close(self): ...
>>>
>>> class Truck(Vehicle):
...     def window_open(self): ...
...     def window_close(self): ...
>>>
>>> class Motorbike(Vehicle):
...     pass
>>>
>>>
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()
>>>
>>> yamaha = Motorbike()
>>> yamaha.engine_start()
>>> yamaha.engine_stop()

Solution 2 - not implemented:

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
...     def window_open(self): ...
...     def window_close(self): ...
>>>
>>> class Car(Vehicle):
...     pass
>>>
>>> class Truck(Vehicle):
...     pass
>>>
>>> class Motorbike(Vehicle):
...     def window_open(self): raise NotImplementedError
...     def window_close(self): raise NotImplementedError
>>>
>>>
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()
>>>
>>> yamaha = Motorbike()
>>> yamaha.engine_start()
>>> yamaha.engine_stop()
>>> yamaha.window_open()
Traceback (most recent call last):
NotImplementedError
>>> yamaha.window_close()
Traceback (most recent call last):
NotImplementedError

16.22.4. Multilevel Inheritance

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
>>>
>>> class VehicleWithWindows(Vehicle):
...     def window_open(self): ...
...     def window_close(self): ...
>>>
>>> class Car(VehicleWithWindows):
...     pass
>>>
>>> class Truck(VehicleWithWindows):
...     pass
>>>
>>> class Motorbike(Vehicle):
...     pass
>>>
>>>
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()
>>>
>>> yamaha = Motorbike()
>>> yamaha.engine_start()
>>> yamaha.engine_stop()

Problem - Passenger take/drop: (let's assume that Truck cannot take passengers)

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
>>>
>>> class VehicleWithWindows(Vehicle):
...     def window_open(self): ...
...     def window_close(self): ...
>>>
>>> class Car(VehicleWithWindows):
...     def passenger_take(self): ...
...     def passenger_drop(self): ...
>>>
>>> class Truck(VehicleWithWindows):
...     pass
>>>
>>> class Motorbike(Vehicle):
...     def passenger_take(self): ...
...     def passenger_drop(self): ...
>>>
>>>
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()
>>>
>>> yamaha = Motorbike()
>>> yamaha.engine_start()
>>> yamaha.engine_stop()

16.22.5. Multilevel Inheritance

>>> class Vehicle:
...     def engine_start(self): ...
...     def engine_stop(self): ...
>>>
>>> class VehicleWithWindows(Vehicle):
...     def window_open(self): ...
...     def window_close(self): ...
>>>
>>> class VehicleWithPassengers(Vehicle):
...     def passenger_take(self): ...
...     def passenger_drop(self): ...
>>>
>>> class Car(VehicleWithWindows, VehicleWithPassengers):
...     pass
>>>
>>> class Truck(VehicleWithWindows):
...     pass
>>>
>>> class Motorbike(VehicleWithPassengers):
...     pass
>>>
>>>
>>>
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>> maluch.passenger_take()
>>> maluch.passenger_drop()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()
>>>
>>> yamaha = Motorbike()
>>> yamaha.engine_start()
>>> yamaha.engine_stop()
>>> yamaha.passenger_take()
>>> yamaha.passenger_drop()

16.22.6. Mixin Classes

>>> class Vehicle:
...     pass
>>>
>>> class HasEngine:
...     def engine_start(self): ...
...     def engine_stop(self): ...
>>>
>>> class HasWindows:
...     def window_open(self): ...
...     def window_close(self): ...
>>>
>>> class HasPassengers:
...     def passenger_take(self): ...
...     def passenger_drop(self): ...
>>>
>>>
>>> class Car(Vehicle, HasEngine, HasWindows, HasPassengers):
...     pass
>>>
>>> class Truck(Vehicle, HasEngine, HasWindows):
...     pass
>>>
>>> class Motorbike(Vehicle, HasEngine, HasPassengers):
...     pass
>>>
>>>
>>> maluch = Car()
>>> maluch.engine_start()
>>> maluch.engine_stop()
>>> maluch.window_open()
>>> maluch.window_close()
>>> maluch.passenger_take()
>>> maluch.passenger_drop()
>>>
>>> scania = Truck()
>>> scania.engine_start()
>>> scania.engine_stop()
>>> scania.window_open()
>>> scania.window_close()
>>>
>>> yamaha = Motorbike()
>>> yamaha.engine_start()
>>> yamaha.engine_stop()
>>> yamaha.passenger_take()
>>> yamaha.passenger_drop()

16.22.7. Use Case - 0x01

>>> class User:
...     pass
>>>
>>> class Admin:
...     pass
>>>
>>>
>>> mark = User()
>>> isinstance(mark, User)
True
>>> isinstance(mark, Admin)
False
>>>
>>>
>>> melissa = Admin()
>>> isinstance(melissa, User)
False
>>> isinstance(melissa, Admin)
True

Single Inheritance:

>>> class User:
...     pass
...
>>> class Admin(User):
...     pass
...
...
>>> mark = User()
>>> isinstance(mark, User)
True
>>> isinstance(mark, Admin)
False
>>>
>>>
>>> melissa = Admin()
>>> isinstance(melissa, User)
True
>>> isinstance(melissa, Admin)
True

Multilevel Inheritance:

>>> class User:
...     pass
...
>>> class Admin(User):
...     pass
...
>>> class SuperAdmin(Admin):
...     pass
>>>
>>>
>>> mark = User()
>>> isinstance(mark, User)
True
>>> isinstance(mark, Admin)
False
>>> isinstance(mark, SuperAdmin)
False
>>>
>>>
>>> melissa = Admin()
>>> isinstance(melissa, User)
True
>>> isinstance(melissa, Admin)
True
>>> isinstance(melissa, SuperAdmin)
False
>>>
>>>
>>> rick = SuperAdmin()
>>> isinstance(rick, User)
True
>>> isinstance(rick, Admin)
True
>>> isinstance(rick, SuperAdmin)
True

Multiple Inheritance:

>>> class CanEditSelf:
...     pass
>>>
>>> class CanEditUsers:
...     pass
>>>
>>> class CanEditAdmins:
...     pass
>>>
>>>
>>> class Account:
...     pass
>>>
>>> class User(Account, CanEditSelf):
...     pass
>>>
>>> class Admin(Account, CanEditSelf, CanEditUsers):
...     pass
>>>
>>> class SuperAdmin(Account, CanEditSelf, CanEditUsers, CanEditAdmins):
...     pass

16.22.8. Assignments

Code 16.28. Solution
"""
* Assignment: OOP InheritancePatterns NoInheritance
* Complexity: easy
* Lines of code: 8 lines
* Time: 3 min

English:
    1. Create classes `Account`, `User`
    2. Do not use inheritance
    3. Assignment demonstrates syntax, so do not add any attributes and methods
    4. Run doctests - all must succeed

Polish:
    1. Stwórz klasy `Account`, `User`
    2. Nie używaj dziedziczenia
    3. Zadanie demonstruje składnię, nie dodawaj żadnych atrybutów i metod
    4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isclass

    >>> assert isclass(Account)
    >>> assert isclass(User)
    >>> assert not issubclass(Account, User)
    >>> assert not issubclass(User, Account)

    >>> assert len(Account.__subclasses__()) == 0
    >>> assert len(User.__subclasses__()) == 0
"""


Code 16.29. Solution
"""
* Assignment: OOP InheritancePatterns Simple
* Type: class assignment
* Complexity: easy
* Lines of code: 4 lines
* Time: 3 min

English:
    1. Create class `Account`
    2. Create class `User` which inherits from `Account`
    3. Run doctests - all must succeed

Polish:
    1. Stwórz klasę `Account`
    2. Stwórz klasę `User`, która dziedziczy po `Account`
    3. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isclass

    >>> assert isclass(Account)
    >>> assert isclass(User)
    >>> assert issubclass(Account, object)
    >>> assert issubclass(User, object)
    >>> assert issubclass(User, Account)
    >>> assert not issubclass(Account, User)

    >>> assert len(Account.__subclasses__()) == 1
    >>> assert len(User.__subclasses__()) == 0
"""


Code 16.30. Solution
"""
* Assignment: OOP InheritancePatterns Multilevel
* Complexity: easy
* Lines of code: 8 lines
* Time: 3 min

English:
    1. Create class `Account`
    2. Create class `User` which inherits from `Account`
    2. Create class `Admin` which inherits from `User`
    3. Run doctests - all must succeed

Polish:
    1. Stwórz klasę `Account`
    2. Stwórz klasę `User`, która dziedziczy po `Account`
    2. Stwórz klasę `Admin`, która dziedziczy po `User`
    3. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isclass

    >>> assert isclass(Account)
    >>> assert isclass(User)
    >>> assert isclass(Admin)

    >>> assert issubclass(Account, object)
    >>> assert issubclass(User, object)
    >>> assert issubclass(User, Account)
    >>> assert issubclass(Admin, object)
    >>> assert issubclass(Admin, User)
    >>> assert issubclass(Admin, Account)

    >>> assert not issubclass(User, Admin)
    >>> assert not issubclass(Account, User)
    >>> assert not issubclass(Account, Admin)

    >>> assert len(Account.__subclasses__()) == 1
    >>> assert len(User.__subclasses__()) == 1
    >>> assert len(Admin.__subclasses__()) == 0
"""

# Create class `Account`
# Create class `User` which inherits from `Account`
# Create class `Admin` which inherits from `User`