Search code examples
pythonpython-typingmypy

Callable is invalid base class?


Can someone explain why inheriting from unparameterized and parameterized Callable:

from typing import Callable
from typing import NoReturn
from typing import TypeVar


T = TypeVar('T', str, int)
C = Callable[[T], NoReturn]


class Foo(Callable):

    def __call__(self, t: T):
        pass


class Bar(C):

    def __call__(self, t: T):
        pass

when passed to mypy raises errors for both Foo and Bar:

tmp.py:13: error: Invalid base class
tmp.py:19: error: Invalid base class

Solution

  • This is in part because classes at runtime can't really inherit from a function or a callable to begin with, and in part because you don't need to explicitly inherit from Callable to indicate that a class is callable.

    For example, the following program typechecks as expected using mypy 0.630:

    from typing import Callable, Union, NoReturn, List
    
    class Foo:
        def __call__(self, t: Union[str, int]) -> NoReturn:
            pass
    
    
    class FooChild(Foo): pass
    
    
    class Bad:
        def __call__(self, t: List[str]) -> NoReturn:
            pass
    
    
    def expects_callable(x: Callable[[Union[str, int]], NoReturn]) -> None: 
        pass
    
    
    expects_callable(Foo())         # No error
    expects_callable(FooChild())    # No error
    expects_callable(Bad())         # Error here: Bad.__call__ has an incompatible signature
    

    Basically, if a class has a __call__ method, it's implicitly assumed that class is also a callable.