Search code examples
pythonpython-3.xpython-typingcallable-object

Difference between Callable and FunctionType


I am trying to properly type hint my code and encountered both Callable and FunctionType

from typing import Callable
def my_func() -> Callable:
    f = lambda: _
    return f

result = my_func()
type(result)  # <class 'function'>
isinstance(result, Callable)  # True

vs

from types import FunctionType
def my_func() -> FunctionType
    f = lambda: ...
    return f

result = my_func()
type(result)  # <class 'function'>
isinstance(result, FunctionType)  # True

One possible case I can think of is to distinguish between regular and class-based callables like this

class C:
    def __call__(self):
        pass

def my_func() -> Callable:
    c = C()
    return c

result = my_func()
type(result)  # <class 'function'>
isinstance(result, Callable)  # True
isinstance(result, FunctionType)  # False

What are the differences between those and when I have to use one over the other?


Solution

  • types.FunctionType is dynamically defined in cpython/Lib/types.py#L11-L12 as the type of the simplest function.

    def _f(): pass
    FunctionType = type(_f)
    

    typing.Callable on the other hand is defined as a wrapped collections.abc.Callable which a) should be used directly if using python >= 3.9 and b) is itself defined in cpython/Lib/_collections_abc.py#L534-L548 as something having a __call__ method.

    class Callable(metaclass=ABCMeta):  # NOTE: I'm skipping most of the definition
    
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Callable:
                return _check_methods(C, "__call__")
            return NotImplemented
    

    You have highlighted correctly that nearly anything can be a callable, just need to give it a __call__ method, on the other hand, a class will never be a function.

    You should use Callable in most cases unless you are completely certain you need a function only.

    Moreover, you can (and should) type the arguments and return value of Callable, for instance:

    • Callable[[], None] -- no arg, returns None (e.g. lambda: None)
    • Callable[[int, int], int] -- two int args, returns an int
    • Callable[..., None] -- any arg, returns None, (e.g. print).