13.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.
13.22.1. Starting Point¶
>>> class Car:
... pass
...
>>> class Truck:
... pass
...
...
>>> maluch = Car()
>>> scania = Truck()
13.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()
13.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
13.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()
13.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()
13.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()
13.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
13.22.8. Assignments¶
"""
* 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(User, Account)
"""
"""
* Assignment: OOP InheritancePatterns NoInheritance
* Complexity: easy
* Lines of code: 8 lines
* Time: 3 min
English:
1. Create classes `MyAccount`, `Account`, `User`, `Admin`
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 `MyAccount`, `Account`, `User`, `Admin`
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 isclass(Admin)
>>> assert isclass(MyAccount)
"""
"""
* Assignment: OOP InheritancePatterns Multilevel
* Complexity: easy
* Lines of code: 8 lines
* Time: 3 min
English:
1. Create class `MyAccount` from classes `Account`, `User`, `Admin`
2. Use multilevel inheritance
3. Assignment demonstrates syntax, so do not add any attributes and methods
4. Run doctests - all must succeed
Polish:
1. Stwórz klasę `MyAccount` z klas `Account`, `User`, `Admin`
2. Użyj wielopoziomowego 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 isclass(Admin)
>>> assert isclass(MyAccount)
>>> assert issubclass(MyAccount, Account)
>>> assert issubclass(MyAccount, User)
>>> assert issubclass(MyAccount, Admin)
>>> assert issubclass(Account, object)
>>> assert issubclass(User, Account)
>>> assert issubclass(Admin, User)
>>> assert issubclass(MyAccount, Admin)
>>> assert len(Account.__subclasses__()) == 1
>>> assert len(User.__subclasses__()) == 1
>>> assert len(Admin.__subclasses__()) == 1
>>> assert len(MyAccount.__subclasses__()) == 0
"""