4.4. Inheritance vs. Composition

  • Mixin Classes

  • Composition over Inheritance

4.4.1. Problem with inheritance

Listing 4.63. Inheritance pattern
class Vehicle:
    def run(self):
        pass

    def drive(self):
        pass

    def window_open(self):
        pass

    def window_close(self):
        pass


class Car(Vehicle):
    pass


class Truck(Vehicle):
    pass
Listing 4.64. Problem with inheritance
 class Vehicle:
     def run(self):
         pass

     def drive(self):
         pass

     def window_open(self):
         pass

     def window_close(self):
         pass


 class Car(Vehicle):
     pass


 class Truck(Vehicle):
     pass


 class Motorbike(Vehicle):
     """Motorbike is a vehicle, but doesn't have windows."""

     def window_open(self):
         raise NotImplementedError

     def window_close(self):
         raise NotImplementedError
class Vehicle:
    def run(self):
        pass

    def drive(self):
        pass


class HasWindows:
    def window_open(self):
        pass

    def window_close(self):
        pass


class Car(Vehicle, HasWindows):
    pass

class Truck(Vehicle, HasWindows):
    pass

class Motorbike(Vehicle):
    pass

4.4.2. Multi level inheritance problem

Listing 4.65. Multi level inheritance is a bad pattern here
class ToJSON:
    def to_json(self):
        import json
        return json.dumps(self.__dict__)


class ToPickle(ToJSON):
    def to_pickle(self):
        import pickle
        return pickle.dumps(self)


class User(ToPickle):
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname


user = User(
    firstname='Jan',
    lastname='Twardowski',
    address='Copernicus Crater, Moon'
)

print(user.to_json())
# {"firstname": "Jan", "lastname": "Twardowski", "address": "Copernicus Crater, Moon"}

print(user.to_pickle())
# b'\x80\x03c__main__\nUser\nq\x00)\x81q\x01}q\x02(X\n\x00\x00\x00firstnameq\x03X\x03\x00\x00\x00Janq\x04X\t\x00\x00\x00lastnameq\x05X\n\x00\x00\x00Twardowskiq\x06X\x07\x00\x00\x00addressq\x07X\x17\x00\x00\x00Copernicus Crater, Moonq\x08ub.'

4.4.3. Composition using Mixin classes

Listing 4.66. Mixin classes - multiple inheritance.
class JSONMixin:
    def to_json(self):
        import json
        return json.dumps(self.__dict__)


class PickleMixin:
    def to_pickle(self):
        import pickle
        return pickle.dumps(self)


class User(JSONMixin, PickleMixin):
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname


user = User(
    firstname='Jan',
    lastname='Twardowski',
    address='Copernicus Crater, Moon'
)

print(user.to_json())
# {"firstname": "Jan", "lastname": "Twardowski", "address": "Copernicus Crater, Moon"}

print(user.to_pickle())
# b'\x80\x03c__main__\nUser\nq\x00)\x81q\x01}q\x02(X\n\x00\x00\x00firstnameq\x03X\x03\x00\x00\x00Janq\x04X\t\x00\x00\x00lastnameq\x05X\n\x00\x00\x00Twardowskiq\x06X\x07\x00\x00\x00addressq\x07X\x17\x00\x00\x00Copernicus Crater, Moonq\x08ub.'

4.4.4. Assignments

4.4.4.1. OOP Composition Mars

English
  1. Create class Habitat

  2. Create class Rocket

  3. Create class Astronaut

  4. Compose class MarsMission from Habitat, Rocket, Astronaut

Polish
  1. Stwórz klasę Habitat

  2. Stwórz klasę Rocket

  3. Stwórz klasę Astronaut

  4. Skomponuj klasę MarsMission z Habitat, Rocket, Astronaut

4.4.4.2. OOP Composition Moveable

English

Todo

English Translation

Polish
  1. Stwórz niemutowalną klasę Point

  2. Klasa Point ma x: int oraz y: int

  3. Gdy x lub y są ujemne (podnieś ValueError)

  4. Stwórz klasę Movable

  5. W klasie Movable zdefiniuj metodę get_position() -> Point

  6. W klasie Movable zdefiniuj metodę set_position(x: int, y: int) -> NoReturn

  7. W klasie Movable zdefiniuj metodę change_position(left: int = 0, right: int = 0, up: int = 0, down: int = 0)

  8. Przyjmij górny lewy róg ekranu za punkt początkowy:

    • idąc w prawo dodajesz x

    • idąc w lewo odejmujesz x

    • idąc w górę odejmujesz y

    • idąc w dół dodajesz y

  9. Testy muszą przechodzić

Tests
"""
>>> class Astronaut(Moveable):
...     pass

>>> astro = Astronaut()
>>> astro.get_position()
Point(x=0, y=0)

>>> astro.change_position(right=10)
>>> astro.get_position()
Point(x=10, y=0)

>>> astro.change_position(left=5)
>>> astro.get_position()
Point(x=5, y=0)

>>> astro.change_position(down=10)
>>> astro.get_position()
Point(x=5, y=10)

>>> astro.change_position(up=5)
>>> astro.get_position()
Point(x=5, y=5)

>>> astro.set_position(x=0, y=0)
>>> astro.get_position()
Point(x=0, y=0)
"""
Hint
  • @dataclass(frozen=True)