Partial Functions in functools Module of Python

Functools is a standard library module of Python. With its help, we can extend the utility of the functions or callable objects without rewriting them. The purpose of functools is to make functional programming more agile. It makes working with high-order functions effortless.

Currently, functools contain these 11 functions.

  1. cmp_to_key()
  2. cached_property()
  3. lru_catch()
  4. partial()
  5. partialmethod()
  6. reduce()
  7. singledispatch()
  8. singledispatchmethod()
  9. total_ordering()
  10. update_wrapper()
  11. wraps()

In this article, we will be having a detailed discussion over the functools partial method of the functools module.

Introduction to partial function

functools partial is a function that takes a function as an argument and some inputs which are pre-filled to return a new function that is prepopulated.

import functools
def sum(a,b):
  return a+b
add_10=functools.partial(sum,10)
print(add_10(10))
20

Syntax of partial function

The partial function takes a function and a preassigned value of a parameter of that function as the arguments.

partial(func: (*args: Any , **kwargs: Any) , *args: Any, **kwargs: Any)

However,, we can write in the following way

partial(sum, 5)

In the above example, the sum is a function that takes two arguments and returns the added product of the two arguments. Simultaneously, we have fixed one of the arguments as 5. Hence, we have developed a new function that can take only one argument, and we will return a product that is the sum of 5 and that argument.

Parameters

The partial method takes these parameters as arguments to return a new function.

ParametersExample
func(*args:Any, **kwargs: Any)e.g. sum(5,9) -> 14
*args: Any, **kwargs: Anye.g., 5, 9

Return Value of the partial function

As notified before, Partial is a function that returns a new function to increase the readability and maintenance of the codebase.

Sample Code:

from functools import partial

def sum(a,b):
    return a+b

add_10 = partial(sum,10)

print(add_10(5))

Output:

15

In the above sample, we can see that a new function add_10 is returned, adding a value of 10 to the passed argument.

Finding the remainder

In this example, we will look at the function that can return the remainder value when a preassigned value of 9 is divided by it.

import math
import functools

mod_2=functools.partial(math.fmod,9)
print(mod_2(5))

Output:

4.0

As mentioned above, a number of reusable functions can be made from the other functions present in the math module.

Simple Interest

In this example, we will be passing an argument and a keyword argument to find the simple interest of a principal sum.

Firstly, let’s discuss the attributes of the partial function, which is extremely useful to gather information about the partial function.

  • partial.args
This attribute returns the arguments that are preassigned to the partial function
  • partial.func
This attribute returns the address along with the name of the parent function
  • partial.keyword
This attribute returns the keywords that are preassigned to the partial function

Example:

from functools import partial

def SI(p,r,t):
    return (p*r*t)/100.0

simple_interest = partial(SI,4,t=2)
print(simple_interest(3000))

print(f'Parent function of simple_interest is: {simple_interest.func}')
print(f'Keyword argument of simple_interest is: {simple_interest.keywords}')
print(f'Argument of simple_interest is: {simple_interest.args}')

Output:

240.0
Parent function of simple_interest is: <function SI at 0x000002627E70F0D0>
Keyword argument of simple_interest is: {'t': 2}
Argument of simple_interest is: (4,)

Common errors to avoid while using partial function

While performing a task using the partial function, always put the keyword argument from the right-hand side. Failing this may lead to “TypeError” as the function will be passed with two values for the same variable.

Example:

from functools import partial

def SI(p,r,t):
    return (p*r*t)/100.0

simple_interest = partial(SI,p=2,r=3)
print(simple_interest(10))

Error:

Traceback (most recent call last):
  File "c:\Users\HP\Desktop\functions\TestPartials.py", line 34, in <module>
    print(simple_interest(10))
TypeError: SI() got multiple values for argument 'p'

functools partial vs lambda function

Often, there is disparity among programmers about these two functions. Though the working of both functions is almost the same, we still find differences in opinions. Here, we will go through the working process of each function in detail and will try to find the best one of them.

lambda

As we know, lambda functions can be used as a function without using def()

x = lambda y: y+1
print(x(1))

Output

2

or it can also be used as a partial function,i.e., it can be used to create a new function out of an existing function with some fixed arguments. However, lambda functions use late binding for the arguments they get. When you make a lambda function passing a variable as an argument, it’s not immediately copied. A reference to the scope is kept, and the value is resolved when the function is called. Thus, if the value of that argument changes within that scope, the lambda function will be called with the adjusted value.

Example:

def sum(x,y):
       return x+y
   
increase = 1
f = lambda n: sum(n,increase)

print(f(1))

Output:

2

Now, let’s change the increment to 3

def sum(x,y):
       return x+y
   
increase = 1
f = lambda n: sum(n,increase)
increase = 3
print(f(1))

Output:

4

Hence, we can conclude that lambda executes the variable during the runtime, and therefore, the value of increment also changed to 3 in the lambda function.

functools partials

Since we are too enlightened with the partial method, we can quickly execute the above code using the partial function to check if there is any difference.

import functools
def sum(x,y):
       return x+y
   
increase = 1
f = functools.partial(sum, increase)

print(f(1))

Output:

2

Changing the value of increment to 3.

import functools
def sum(x,y):
       return x+y
   
increase = 1
f = functools.partial(sum,increase)
increase = 3
print(f(1))

Output:

2

Here, it is evident that there is no change in output with a change in the variable. This is because the partial method executes the variable whenever it is called.

Note: partial method turns out to be better than lambda function because it is more explicit and can easily be deployed in the scenario of pipe-lined sequence.

What are keyword arguments in the partial method?

It is an attribute of the partial function that returns the keywords that are preassigned to the function.

Can we use functools partial as asynchronous functions?

Yes, one can use the partial method as asynchronous functions. Let’s go through an example.

import asyncio
from functools import partial
async def sum(a,b):
print(a+b)
return a+b
add_10 = partial(sum,10)
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(add_10(1)),
loop.create_task(add_10(2)),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()


Output:
11
12

Conclusion

Partial is an advantageous method. Especially when there is a scenario where a value from one function has to be passed to another function(pipe-lined sequence), its need will be well portrayed.

In this article, we covered every bit and piece necessary to implement the function in real-life projects. Now, it’s your time to show off your skills with the help of partial function.

Bon Codage!

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments