Updated On : Nov-14,2019 Time Investment : ~25 mins

functools: Functions

functools module provides functions that act on other functions and returns modified functions. It lets us add more functionality by providing a wrapper around existing functions.

We'll be covering this tutorial in 2 parts. In this part, we'll be covering functions of this module and in 2nd part, we'll be covering annotations available for the same functionalities.

import functools
import datetime
import pytz
import time

functools functions:

  • functools.cmp_to_key(func) - It converts old-style comparison function to key function.

Comparison function takes 2 arguments and returns negative, zero and positive numbers based on less than, equal and greater than the relationship between 2 elements. The key function takes single element and returns a value that will then be used for sorting.

class Employee(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return self.name + ' : , Age : '+str(self.age)

e1 = Employee('Sunny',27)
e2 = Employee('Sumit',27)
e3 = Employee('Anup',35)
e4 = Employee('Ashwin',35)
e5 = Employee('Rohit',38)
e6 = Employee('Irfan',38)
e7 = Employee('Ethesh',22)
e8 = Employee('Kaushal',45)

def emp_cmp(emp1,emp2):
    return 1 if emp1.age > emp2.age else -1 if emp1.age < emp2.age else 0

sorted_employees = sorted([e8,e7,e6,e5,e4,e3,e2,e1], key = functools.cmp_to_key(emp_cmp))
print([str(emp) for emp in sorted_employees]) ## Please make a not that it's stable sort as it maintained same order for employees with same age.
['Ethesh : , Age : 22', 'Sumit : , Age : 27', 'Sunny : , Age : 27', 'Ashwin : , Age : 35', 'Anup : , Age : 35', 'Irfan : , Age : 38', 'Rohit : , Age : 38', 'Kaushal : , Age : 45']
  • functools.partial(func,*args,**kwargs) - It wraps function provided to it with arguments provided as args & kwargs set and returns partial object. It's generally used to freeze few arguments of some function with too many arguments so that one can avoid giving the same arguments again and again.
  • functools.partialmethod(func,*args,**kwargs) - It returns a new partialmethod descriptor which is the same as partial object. It's designed to be method definition in class than directly being called partial. It freezes all parameters.
freezed_params = {'hour':5,'minute':0,'second':59,'microsecond':999}
partial_datetime = functools.partial(datetime.datetime, **freezed_params)
partial_datetime(2015,1,1), partial_datetime(2018,2,21), partial_datetime(2017,1,31)
(datetime.datetime(2015, 1, 1, 5, 0, 59, 999),
 datetime.datetime(2018, 2, 21, 5, 0, 59, 999),
 datetime.datetime(2017, 1, 31, 5, 0, 59, 999))
partial_datetime.func, partial_datetime.args, partial_datetime.keywords
(datetime.datetime,
 (),
 {'hour': 5, 'minute': 0, 'second': 59, 'microsecond': 999})
class Employee(object):
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def decrease_salary(self, percentage):
        self.salary -= (self.salary*(percentage/100))
    def increase_salary(self, percentage):
        self.salary += (self.salary*(percentage/100))

    highest_performar_increase = functools.partialmethod(increase_salary, 30)
    average_performar_increase = functools.partialmethod(increase_salary, 15)
    bad_performar_decrease = functools.partialmethod(decrease_salary, 5)

e1 = Employee('Sumit', 25000)
e2 = Employee('Jayesh', 35000)
e3 = Employee('Rohit', 35000)
e1.highest_performar_increase(), e2.average_performar_increase(), e3.bad_performar_decrease()
e1.salary, e2.salary, e3.salary
(32500.0, 40250.0, 33250.0)
  • functools.reduce(function,iterable[,initializer]) - It applies function of 2 arguments cummunlatively to all elements of iterable from left to right to return single value.If initializer is provided then function is performed first on initializer and first element of iterable.
print(functools.reduce(lambda x,y : x - y, range(1,5))) # (((1-2)-3)-4)
print(functools.reduce(lambda x,y : x - y, range(1,5), 100 )) # ((((100-1)-2)-3)-4)
-8
90
  • functools.update_wrapper(wrapper, wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES) - It assigns/updates atrributes of wrapped(original function) to wrapper function.
functools.WRAPPER_ASSIGNMENTS, functools.WRAPPER_UPDATES
(('__module__', '__name__', '__qualname__', '__doc__', '__annotations__'),
 ('__dict__',))
def outer(func):
    def inner(*args):
        print('Before execution')
        func(*args)
        print('After execution')
    return inner

def print_title(title):
    """
    Simple print functionality
    """
    print(title)

wrapper = outer(print_title)
print('Attributes before update : '+ str(dict(zip(['__module__', '__name__', '__qualname__', '__doc__', '__annotations__','__dict__'],
                                                  [wrapper.__module__,wrapper.__name__, wrapper.__qualname__, wrapper.__doc__, wrapper.__annotations__, wrapper.__dict__]))))
functools.update_wrapper(wrapper, print_title)
print('\nAttributes after update : '+ str(dict(zip(['__module__', '__name__', '__qualname__', '__doc__', '__annotations__','__dict__'],
                                                  [wrapper.__module__,wrapper.__name__,wrapper.__qualname__, wrapper.__doc__, wrapper.__annotations__, wrapper.__dict__]))))
Attributes before update : {'__module__': '__main__', '__name__': 'inner', '__qualname__': 'outer.<locals>.inner', '__doc__': None, '__annotations__': {}, '__dict__': {}}

Attributes after update : {'__module__': '__main__', '__name__': 'print_title', '__qualname__': 'print_title', '__doc__': '\n    Simple print functionality\n    ', '__annotations__': {}, '__dict__': {'__wrapped__': <function print_title at 0x7fbb319fed90>}}
Sunny Solanki  Sunny Solanki

Share Views Stuck Somewhere? Need Help with Coding? Have Doubts About the Topic/Code?

When going through coding examples, it's quite common to have doubts and errors.

If you have doubts about some code examples or are stuck somewhere when trying our code, send us an email at coderzcolumn07@gmail.com. We'll help you or point you in the direction where you can find a solution to your problem.

You can even send us a mail if you are trying something new and need guidance regarding coding. We'll try to respond as soon as possible.

Share Views Want to Share Your Views? Have Any Suggestions?

If you want to

  • provide some suggestions on topic
  • share your views
  • include some details in tutorial
  • suggest some new topics on which we should create tutorials/blogs
Please feel free to contact us at coderzcolumn07@gmail.com. We appreciate and value your feedbacks. You can also support us with a small contribution by clicking DONATE.