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 Multilevel
* Complexity: easy
* Lines of code: 8 lines
* Time: 3 min
English:
1. Create class `MarsMission` from classes `Habitat`, `Rocket`, `Astronaut`
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ę `MarsMission` z klas `Habitat`, `Rocket`, `Astronaut`
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(Habitat)
>>> assert isclass(Astronaut)
>>> assert isclass(Rocket)
>>> assert isclass(MarsMission)
>>> assert issubclass(MarsMission, Habitat)
>>> assert issubclass(MarsMission, Astronaut)
>>> assert issubclass(MarsMission, Rocket)
>>> assert len(Habitat.__subclasses__()) == 1
>>> assert len(Astronaut.__subclasses__()) == 1
>>> assert len(Rocket.__subclasses__()) == 1
>>> assert len(MarsMission.__subclasses__()) == 0
"""
"""
* Assignment: OOP InheritancePatterns Mixin
* Complexity: easy
* Lines of code: 8 lines
* Time: 3 min
English:
1. Create class `MarsMission` from classes `Habitat`, `Rocket`, `Astronaut`
2. Use mixins classes
3. You can modify given classes
4. Assignment demonstrates syntax, so do not add any attributes and methods
5. Run doctests - all must succeed
Polish:
1. Stwórz klasę `MarsMission` z klas `Habitat`, `Rocket`, `Astronaut`
2. Użyj klas domieszkowych (mixin)
3. Możesz modyfikować dane klasy
4. Zadanie demonstruje składnię, nie dodawaj żadnych atrybutów i metod
5. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isclass
>>> assert isclass(Habitat)
>>> assert isclass(Astronaut)
>>> assert isclass(Rocket)
>>> assert isclass(MarsMission)
>>> assert issubclass(MarsMission, Habitat)
>>> assert issubclass(MarsMission, Astronaut)
>>> assert issubclass(MarsMission, Rocket)
>>> assert len(Habitat.__subclasses__()) == 1
>>> assert len(Astronaut.__subclasses__()) == 1
>>> assert len(Rocket.__subclasses__()) == 1
>>> assert len(MarsMission.__subclasses__()) == 0
"""