Introduction#
The goal of today’s post is to dig into the Python’s functools.partial object.
Partial Basics#
Let’s start by importing partial from Python’s built-in functools.
>>> from functools import partial
Next, we define a simplistic multiply method and a times_two partial object which fixes the multiplier to always be 2.
>>> def multiply(a: int, b: int) -> int:
... return a * b
>>> times_two = partial(multiply, b=2)
>>> times_two(3)
6
When inspecting the __name__ and __doc__ fields that regular Python methods always provide, we notice that for partial objects __name__ does not exists and __doc__ is set to some generic partial object default.
So to have sensible values for the two fields, we have to set those ourselves.
>>> times_two.__name__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'functools.partial' object has no attribute '__name__'. Did you mean: '__ne__'?
>>> times_two.__name__ = 'times_two'
>>> times_two.__name__
'times_two'
>>> times_two.__doc__
'partial(func, *args, **keywords) - new function with partial application\n of the given arguments and keywords.\n'
>>> times_two.__doc__ = 'Multiply the given value times two.'
>>> times_two.__doc__
'Multiply the given value times two.'
When printing our times_two partial we get the name of the class together with a reference to the original method and the arguments that we’ve fixed for our partial.
>>> times_two
functools.partial(<function multiply at 0x101358900>, b=2)
We can also get those latter two explicitly, using the .func and keywords fields of partial.
>>> times_two.func
<function multiply at 0x101358900>
>>> times_two.keywords
{'b': 2}
If we had used partial with non-keyword arguments (like partial(multiply, 2), which would’ve set a to 2), those values would show up in the .args field.
Since we’ve only used keyword arguments however, args will be an empty tuple in our case.
>>> times_two.args
()
Partial With Types#
If we ask Python for the type of the partial, it will simply tell us it’s a functools.partial object.
>>> type(times_two)
<class 'functools.partial'>
And while we can get the argument names with their types as well the return type from a regular Python method using <method_name>.__annotations__, this dunder method is not provided for partial objects.
>>> times_two.__annotations__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'functools.partial' object has no attribute '__annotations__'
However, we can retrieve the same output using the simple dict comprehension below.
>>> {k: v for k, v in times_two.func.__annotations__.items() if k not in times_two.keywords}
{'a': <class 'int'>, 'return': <class 'int'>}
