Skip to content

Instantly share code, notes, and snippets.

@MattOates
Last active July 24, 2020 13:04
Show Gist options
  • Save MattOates/33fa709d8313e59493647e1b492f6950 to your computer and use it in GitHub Desktop.
Save MattOates/33fa709d8313e59493647e1b492f6950 to your computer and use it in GitHub Desktop.
Observer pattern in Python, but has some annoying problems with respect to mypy typing
from typing import Any, Callable, Dict, List, Optional, Set
from abc import ABC, abstractmethod
import logging
LOGGER = logging.getLogger(__name__)
class Observer(ABC):
@abstractmethod
def update(self, observed: "Observable", event: Any) -> None:
pass
class Logger(Observer):
def update(self, observed: "Observable", event: Any) -> None:
if isinstance(observed, "ImplementedThing") and isinstance(event, str):
LOGGER.warning(f"{observed.description} WARNING: {event}")
class Observable(ABC):
_default_observers: Set[Observer] = {Logger()}
_observers: Set[Observer]
def __init__(
self,
using: Optional[List[Observer]] = None
):
self._observers = set()
for observer in Observable._default_observers:
self.subscribe(observer)
if using:
for observer in using:
self.subscribe(observer)
def subscribe(self, observer: Observer) -> None:
self._observers.add(observer)
def unsubscribe(self, observer: Observer) -> None:
self._observers.remove(observer)
def publish(self, event: object) -> None:
for observer in self._observers:
observer.update(self, event)
class ImplementedThing(Observable):
description: str = "Hello World"
def __init__(self, description: str):
super().__init__()
self.description = description
def implement(self):
print(self.description)
self.publish(self.description)
thing = ImplementedThing("Goodbye")
thing.implement()
@MattOates
Copy link
Author

With the above code we get the error:

observable.py:15: error: Argument 1 of "update" is incompatible with supertype "Observer"; supertype defines the argument type as "Observable"
observable.py:15: note: This violates the Liskov substitution principle
observable.py:15: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
Found 1 error in 1 file (checked 1 source file)

However if you attempt to resolve this its impossible to avoid a This violates the Liskov substitution principle which isn't strictly true as the more general class is an abstract class and can never be instantiated. So there is no substitution here strictly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment