Dependency inversion principle
This principle mainly state that, a module should "Depend upon abstractions, [not] concretions.", and more precisely:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
 - Abstractions should not depend on details. Details should depend on abstractions.
 
Code example
  
    from dataclasses import dataclass
    from abc import abstractmethod
    from typing import List
    
    
    # Bad implementation
    @dataclass
    class Dog():
        size: str
    
        @abstractmethod
        def bark(self):
            pass
    
    
    @dataclass
    class BigDog(Dog):
        size: str = "big"
    
        @abstractmethod
        def bark(self):
            print("WOF WOF")
    
    
    @dataclass
    class SmallDog(Dog):
        size: str = "small"
    
        @abstractmethod
        def bark(self):
            print("wof wof")
    
    
    def barks(size_of_dog: str):
        if size_of_dog == "small":
            dog = SmallDog()
            dog.bark()
        elif size_of_dog == "big":
            dog = BigDog()
            dog.bark()
        else:
            raise NotImplementedError
    
    
    barks("big")
  
  
  
    # Good implementation
    @dataclass
    class Dog():
        size: str
    
        @abstractmethod
        def bark(self):
            pass
    
    
    @dataclass
    class BigDog(Dog):
        size: str = "big"
    
        @abstractmethod
        def bark(self):
            print("WOF WOF")
    
    
    @dataclass
    class SmallDog(Dog):
        size: str = "small"
    
        @abstractmethod
        def bark(self):
            print("wof wof")
    
    
    def barks(dog: Dog):
        dog.bark()
    
    
    barks(BigDog())
  
  References
- https://en.wikipedia.org/wiki/Dependency_inversion_principle
 - "Clean Architecture: A Craftsman's Guide to Software Structure and Design" by Robert Martin
 - https://stackoverflow.com/questions/61358683/dependency-inversion-in-python