5.10. OOP Inheritance Patterns¶
no inheritance
single inheritance
multilevel inheritance
multiple inheritance (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.
5.10.1. No Inheritance¶
>>> class Vehicle:
... pass
>>>
>>>
>>> class Car:
... pass
5.10.2. Single Inheritance¶
>>> class Vehicle:
... pass
>>>
>>>
>>> class Car(Vehicle):
... pass
5.10.3. Multilevel Inheritance¶
>>> class Vehicle:
... pass
>>>
>>>
>>> class VehicleWithWindows(Vehicle):
... pass
>>>
>>>
>>> class Car(VehicleWithWindows):
... pass
5.10.4. Multiple Inheritance¶
HasEngine
andHasWindows
are Mixin ClassesSuch classes are usually called:
EngineMixin
,WindowsMixin
>>> class Vehicle:
... pass
>>>
>>> class HasEngine:
... pass
>>>
>>> class HasWindows:
... pass
>>>
>>>
>>> class Car(Vehicle, HasEngine, HasWindows):
... pass
...
5.10.5. Composition¶
>>> class Vehicle:
... pass
>>>
>>> class Engine:
... pass
...
>>> class Windows:
... pass
...
>>>
>>> class Car(Vehicle):
... engine = Engine
... windows = Windows
5.10.6. Aggregation¶
>>> class Vehicle:
... pass
...
>>> class Engine:
... pass
...
>>> class Windows:
... pass
...
>>>
>>> class Car(Vehicle):
... parts = [Engine, Windows]
5.10.7. Why Composition?¶
>>> class Mother:
... pass
>>>
>>> class Father:
... pass
>>>
>>>
>>> class Child:
... mother: Mother
... father: Father
...
... def __init__(self, mother=Mother(), father=Father()):
... self.mother = mother
... self.father = father
>>> class StepFather:
... pass
>>>
>>> me = Child(father=StepFather())
5.10.8. Use Case - 0x01¶
Following example is simple and easy to understand, but not totally
accurate. Inheritance means, that a class is a specialized form of
its base. This results in a subclass being an instance of a superclass.
Which is weird when we think, that a Child
might be its Parent
in the same time.
No Inheritance:
>>> class Parent:
... pass
>>>
>>>
>>> class Child:
... pass
Single Inheritance:
>>> class Parent:
... pass
>>>
>>>
>>> class Child(Parent):
... pass
Multilevel Inheritance:
>>> class Grandparent:
... pass
>>>
>>> class Parent(Grandparent):
... pass
>>>
>>>
>>> class Child(Parent):
... pass
Multiple Inheritance:
>>> class Mother:
... pass
>>>
>>> class Father:
... pass
>>>
>>>
>>> class Child(Mother, Father):
... pass
Composition:
>>> class Mother:
... pass
>>>
>>> class Father:
... pass
>>>
>>> class Child:
... mother = Mother
... father = Father
Aggregation:
>>> class Mother:
... pass
>>>
>>> class Father:
... pass
>>>
>>> class Child:
... parents = [Father, Mother]
5.10.9. Use Case - 0x02¶
>>> class Mother:
... pass
>>>
>>> class Father:
... pass
>>>
>>>
>>> class Child:
... mother: Mother
... father: Father
...
... def __init__(self, mother=Mother(), father=Father()):
... self.mother = mother
... self.father = father
5.10.10. Use Case - 0x03¶
>>> class Vehicle:
... engine: Engine
... windows: Windows | None
>>>
>>> class Engine:
... def engine_start(self): ...
... def engine_stop(self): ...
...
>>> class Windows:
... def window_open(self): ...
... def window_close(self): ...
...
>>>
>>> class Car(Vehicle):
... engine: Engine
... windows: Windows
...
... def __init__(self, windows=Windows(), engine=Engine()):
... self.windows = windows
... self.engine = engine
...
... def engine_start(self):
... if self.engine:
... return self.engine.engine_start()
...
... def engine_stop(self):
... if self.engine:
... return self.engine.engine_stop()
...
... def window_open(self):
... if self.windows:
... return self.windows.windows_open()
...
... def window_close(self):
... if self.windows:
... return self.windows.windows_close()
5.10.11. Use Case - 0x04¶
>>> class Encoder:
... def encode(self, data):
... ...
>>>
>>> class Decoder:
... def decode(self, data):
... ...
>>>
>>>
>>> class JSONSerializer:
... encoder: Encoder
... decoder: Decoder
...
... def __init__(self,
... encoder: Encoder = Encoder(),
... decoder: Decoder = Decoder(),
... ) -> None:
... self.encoder = encoder
... self.decoder = decoder
...
... def encode(self, data):
... return self.encoder.encode(data)
...
... def decode(self, data):
... return self.decoder.decode(data)
>>>
>>>
>>> DATA = {'firstname': 'Mark', 'lastname': 'Watney'}
Now, if you want to serialize your data, just create an instance
and call method .encode()
on it.
>>> json = JSONSerializer()
>>> result = json.encode(DATA)
If you want to use your better version of encoder (for example which
can encode datetime
object. You can create a class which inherits
from default Encoder
and overwrite .encode()
method.
>>> class MyBetterEncoder(Encoder):
... def encode(self):
... ...
>>>
>>> json = JSONSerializer(encoder=MyBetterEncoder)
>>> result = json.encode(DATA)
5.10.12. Use Case - 0x05¶
>>> from datetime import date
>>> import json
>>> DATA = {'firstname': 'Mark', 'lastname': 'Watney'}
>>>
>>> json.dumps(DATA)
'{"firstname": "Mark", "lastname": "Watney"}'
>>> DATA = {'firstname': 'Mark', 'lastname': 'Watney', 'birthday': date(1969, 7, 21)}
>>>
>>> json.dumps(DATA)
Traceback (most recent call last):
TypeError: Object of type date is not JSON serializable
>>> class Encoder(json.JSONEncoder):
... def default(self, x):
... if isinstance(x, date):
... return x.isoformat()
...
>>>
>>> DATA = {'firstname': 'Mark', 'lastname': 'Watney', 'birthday': date(1969, 7, 21)}
>>>
>>> json.dumps(DATA, cls=Encoder)
'{"firstname": "Mark", "lastname": "Watney", "birthday": "1969-07-21"}'
5.10.13. Further Reading¶
5.10.14. Assignments¶
"""
* Assignment: OOP InheritancePatterns Simple
* Required: yes
* 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
"""
"""
* Assignment: OOP InheritancePatterns Multiple
* Complexity: easy
* Lines of code: 8 lines
* Time: 3 min
English:
1. Create class `MyAccount` from classes `Account`, `User`, `Admin`
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ę `MyAccount` z klas `Account`, `User`, `Admin`
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(Account)
>>> assert isclass(User)
>>> assert isclass(Admin)
>>> assert isclass(MyAccount)
>>> assert issubclass(MyAccount, Account)
>>> assert issubclass(MyAccount, 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
"""
"""
* Assignment: OOP InheritancePatterns Composition
* Complexity: easy
* Lines of code: 10 lines
* Time: 3 min
English:
1. Create class `MyAccount` from classes `Account`, `User`, `Admin`
2. Use composition
3. Assignment demonstrates syntax, so do not add any attributes and methods (only type annotations)
4. Run doctests - all must succeed
Polish:
1. Stwórz klasę `MyAccount` z klas `Account`, `User`, `Admin`
2. Użyj kompozycji
3. Zadanie demonstruje składnię, nie dodawaj żadnych atrybutów i metod (tylko anotacje typów)
4. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isclass
>>> assert isclass(User)
>>> assert isclass(Admin)
>>> assert isclass(Account)
>>> assert isclass(MyAccount)
>>> assert hasattr(MyAccount, 'account')
>>> assert hasattr(MyAccount, 'user')
>>> assert hasattr(MyAccount, 'admin')
>>> assert isinstance(MyAccount.account, Account)
>>> assert isinstance(MyAccount.user, User)
>>> assert isinstance(MyAccount.admin, Admin)
"""