Skip to content

← Precise Type Hints with Literals and Generics step 18 of 26

Easy Primitives

Catch a Renamed Method with @override

A teammate renames a method in a base class, or misspells it in a subclass. The subclass method no longer replaces the base one, and Python reports no error:

class Notifier:
    def deliver(self, message: str) -> bool:
        return False


class EmailNotifier(Notifier):
    def delivr(self, message: str) -> bool:
        return 0 < len(message) <= 100


EmailNotifier().deliver("hi")  # False: the base method still runs

Put @override (from typing) on the subclass method. It tells mypy that this method must replace a method of the same name in a base class. mypy then reports the typo:

Method "delivr" is marked as an override, but no base method was found with this name

mypy checks that the two signatures are compatible for every method that replaces a base method, with or without @override. At runtime, @override changes nothing.

What to write

The starter's Notifier is an abstract base class. Its deliver is an @abstractmethod, so a subclass must implement it before you can create an instance.

  • Rename EmailNotifier.delivr to deliver, and put @override on it.
  • Write SmsNotifier(Notifier) with an @override deliver method.
  • EmailNotifier.deliver(message) returns True when 0 < len(message) <= 100, and False otherwise.
  • SmsNotifier.deliver(message) returns True when 0 < len(message) <= 20, and False otherwise.
  • Count characters with len. Do not trim the message, so " " has length 1. The methods send nothing.
  • dispatch(notifiers: Sequence[Notifier], message: str) -> list[bool] calls each notifier's deliver once, in order, and returns the results in the same order. Repeated notifiers each get a result. If a notifier raises, let the exception propagate.

What solve returns

solve(kinds: list[str], message: str) -> tuple[list[bool], int, list[str]] reads kinds in order. For each exact "email" or "sms", it creates that notifier. It does not change case or trim, so "EMAIL" is not "email". Every other kind goes into a skipped list, in order and with duplicates.

It returns a tuple of three values:

  1. The dispatch results for the notifiers it created.
  2. How many results are True. sum(results) counts them.
  3. The skipped kinds.
assert solve(["email", "sms"], "hello") == ([True, True], 2, [])
assert solve(["sms", "email"], "x" * 21) == ([False, True], 1, [])
assert solve(["EMAIL", "email", "push", "push"], " ") == ([True], 1, ["EMAIL", "push", "push"])
assert solve([], "hello") == ([], 0, [])

Going further

mypy --enable-error-code explicit-override also reports any overriding method that lacks @override. The site's grader does not turn it on, but you can run it locally to check that both deliver methods have the decorator.