Last active
April 29, 2019 13:49
-
-
Save icedraco/05f0cbc5add1d1d848d1150ca2d91e0b to your computer and use it in GitHub Desktop.
Python Dependency Injection via MRO/super() demo based on Raymong Hettinger talk during PyCon 2015
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This example demonstrates a "dependency injection" technique described | |
by Raymond Hettinger during PyCon 2015: | |
https://www.youtube.com/watch?v=EiOglTERPEo | |
Regular dependency injection requires one to provide a resource to an object | |
that requires it using the __init__() method (or a class constructor). | |
This method provides an alternative way to specify a resource using Python's | |
Method Resolution Order (MRO) and super() magic. | |
""" | |
from abc import ABC, abstractmethod | |
from typing import Iterable | |
# ---------------------------- | |
class AbstractItemSource(ABC): | |
@abstractmethod | |
def get_items(self) -> Iterable[str]: | |
raise NotImplementedError() | |
class DefaultItemSource(AbstractItemSource): | |
def __init__(self, url: str): | |
self.url = url | |
def get_items(self) -> Iterable[str]: | |
yield f"First default item ({self.url})" | |
yield f"Second default item ({self.url})" | |
class AlternateItemSource(AbstractItemSource): | |
def __init__(self, filename: str): | |
self.filename = filename | |
def get_items(self) -> Iterable[str]: | |
yield f"First alt item ({self.filename})" | |
yield f"Second alt item ({self.filename})" | |
yield f"Third alt item ({self.filename})" | |
# ---------------------------- | |
class AbstractItemReader: | |
""" | |
AbstractItemReader instance that reads items from an arbitrary | |
AbstractItemSource provided using "multiple inheritance" syntax | |
(see implementations below) | |
""" | |
def read(self): | |
print(f"{self.__class__.__name__}:") | |
for item in self.get_items(): | |
print(f" > {item}") | |
print() | |
class DefaultItemReader(AbstractItemReader, DefaultItemSource): | |
""" | |
AbstractItemReader instance with DefaultItemSource as the | |
item source [get_items()] | |
""" | |
def __init__(self, url: str): | |
super().__init__(url) | |
class AltItemReader(AbstractItemReader, AlternateItemSource): | |
""" | |
AbstractItemReader instance with AlternateItemSource as the | |
item source [get_items()] | |
""" | |
def __init__(self, filename: str): | |
super().__init__(filename) | |
# ---------------------------- | |
if __name__ == "__main__": | |
DefaultItemReader('http://url.com').read() | |
AltItemReader('file.txt').read() | |
# ---------------------------- | |
# Result | |
# ---------------------------- | |
# | |
# DefaultItemReader: | |
# > First default item (http://url.com) | |
# > Second default item (http://url.com) | |
# | |
# AltItemReader: | |
# > First alt item (file.txt) | |
# > Second alt item (file.txt) | |
# > Third alt item (file.txt) | |
# |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment