Skip to content

Instantly share code, notes, and snippets.

@icedraco
Last active April 29, 2019 13:49
Show Gist options
  • Save icedraco/05f0cbc5add1d1d848d1150ca2d91e0b to your computer and use it in GitHub Desktop.
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 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