Search code examples
pythonfunctools

Why is functools.partial not a subclass of types.FunctionType?


In my code I need to detect if a variable is a function or not and preform some actions on it.

Everything went well until I now created a partial function using functools and suddenly some of my tests fail:

import types
import functools

def f(s):
    print(s)

l = lambda s: print(s)

pf = functools.partial(f, 'Hello World')
pl = functools.partial(l, 'Hello World')
test_f = isinstance(f, types.FunctionType) # True
test_l = isinstance(l, types.FunctionType) # True
test_pf = isinstance(pf, types.FunctionType) # False
test_pl = isinstance(pl, types.FunctionType) # False

Why is there a difference between those? Both varieties are callable... Even more importantly, how can I detect if some variable is a function or not even when it's a partial function if I can't use types.FunctionType?


Solution

  • functools.partial is a class with a __call__ method, it says in the documents:

    Return a new partial object which when called will behave like func

    (bold emphasis added by me)

    We can confirm this in our Python REPL:

    >>> from functools import partial
    >>> add_one = partial(sum, 1)
    >>> type(add_one)
    <class 'functools.partial'>
    

    A Python equivalent would be something like this:

    class Partial:
    
        def __init__(self, func, *args, **kwargs):
            self.func = func
            self.args = args
            self.kwargs = kwargs
    
        def __call__(self, *args, **kwargs):
            return self.func(*self.args, *args, **self.kwargs, **kwargs)
    

    So it creates a simple wrapper object around the function, and an object like this simply isn't a function. types.FunctionType only works on actual functions.

    What you're looking for is a way to check if an object is callable, for that you can use the built-in callable function:

    >>> callable(sum)
    True
    >>> callable(3)
    False
    >>> callable(functools.partial(sum, 1))
    True