Question: I would like to use functools.singledispatchmethod to overload the binary arithmetic operator methods of a class called Polynomial. The problem I have is that I can't find a way to register method calls where other is a Polynomial. Perhaps better explained with a simple example: from __future__ import annotations from functools import singledispatchmethod class Polynomial: @singledispatchmethod def __add__(self, other): return NotImplemented @__add__.register def _(self, other: Polynomial): return NotImplemented  The code above raises a NameError: NameError: name 'Polynomial' is not defined  This NameError is not caused by the annotation but is raised inside functools. Also, annotating using a string 'Polynomial' instead without the need of __future__.annotations doesn't work either. This behavior is not documented in the documentation for functools.singledispatchmethod. I can make it work by making Polynomial inherit from another class, and then using that class in the type annotation: from functools import singledispatchmethod class _Polynomial: pass class Polynomial(_Polynomial): @singledispatchmethod def __add__(self, other): return NotImplemented @__add__.register def _(self, other: _Polynomial): return NotImplemented  ..but I am not overly fond of this solution. How can I make this work without needing the useless intermediate class?

Answer: The alternative is to add the methods after the class definition: from __future__ import annotations from functools import singledispatchmethod class Polynomial: pass @singledispatchmethod def __add__(self, other): return NotImplemented @__add__.register def _(self, other: Polynomial): return NotImplemented Polynomial.__add__ = __add__  You can't actually reference the class during a class definition, because the class doesn't exist yet. Using the from __future__ import annotations makes annotations get saved as strings, but as soon as the decorator tries to evaluate those strings for their values, the same problem occurs, so it doesn't get around it (because it isn't a mere annotation). This is evident in the stack trace (abbreviated): ~/miniconda3/envs/py39/lib/python3.9/functools.py in register(cls, func) 858 # only import typing if annotation parsing is necessary 859 from typing import get_type_hints --> 860 argname, cls = next(iter(get_type_hints(func).items())) 861 if not isinstance(cls, type): 862 raise TypeError( ~/miniconda3/envs/py39/lib/python3.9/typing.py in get_type_hints(obj, globalns, localns, include_extras) 1447 if isinstance(value, str): 1448 value = ForwardRef(value) -> 1449 value = _eval_type(value, globalns, localns) 1450 if name in defaults and defaults[name] is None: 1451 value = Optional[value] ~/miniconda3/envs/py39/lib/python3.9/typing.py in _eval_type(t, globalns, localns, recursive_guard) 281 """ 282 if isinstance(t, ForwardRef): --> 283 return t._evaluate(globalns, localns, recursive_guard) 284 if isinstance(t, (_GenericAlias, GenericAlias)): 285 ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__) ~/miniconda3/envs/py39/lib/python3.9/typing.py in _evaluate(self, globalns, localns, recursive_guard) 537 localns = globalns 538 type_ =_type_check( --> 539 eval(self.__forward_code__, globalns, localns), 540 "Forward references must evaluate to types.", 541 is_argument=self.__forward_is_argument__,  So, when the singledispatchmethod decorater typing.get_type_hints, which is essentially eval (with the added feature of taking care to obtain the correct global scope to evaluate the string in, the one where the annotation was made)