Functools Module in Python

feature-image-functools.png

The functools module in Python provides higher-order functions and operations on callable objects. It offers tools for working with functions as first-class objects, enabling function transformation, adaptation, and extension without directly modifying their code. It is one of those modules that may seem simple at first glance, but unlocks powerful tricks to work with your functions more efficiently. Whether you are building a web application, writing data processing scripts, or automating tasks, functools can help you optimize performance and write cleaner, more understandable code. In this blog, you will learn what the functools module offers.

Table of Contents:

What is the functools Module in Python?

Higher-order functions in Python can act on or return other functions. To perform such high-order functions in Python, you can utilize the functools module. This module is a standard library in Python that provides you with tools that make it easier to work with/ change/ reuse functions in ways that usually would not be possible with simple functions alone.

Why Should You Use functools in Python?

You should use functools to optimize your code, reuse functions with fixed arguments, cache expensive computations, or modify functions without changing their original structure.

It makes your programs more efficient, reduces code repetition, and helps you work smarter, not harder.

Importing functools in Python

Before using any functions from the functools module, you need to import it first. Python makes it simple and efficient by bringing the functools module into consideration.

Syntax:

import functools
Master Python and Elevate Your Tech Skills
Learn from industry pros and build real-world projects
quiz-icon

Caching Results Using functools.lru_cache() in Python 

Your functions can take a long time to compute because they perform complex work. In this case, lru_cache helps in remembering the results for you to save time. Thus, if you call the same function again with the same inputs, you don’t need to wait again.

Syntax:

@functools.lru_cache(maxsize=None)
def function_name(parameters):
    # function body

Example:

Python

Output:

Caching Results Using functools

Explanation: Here, the function performs a computation to multiply numerals (8*8) and caches the result in the first call. In the second call with the same input, it does not recompute it, but instead fetches the result from the cache.

Fixing Arguments Using functools.partial() in Python

There are times when you always call a function with the same value for one or two arguments. You can use partial() to fix those arguments ahead of time and create a shortcut without the need to repeat yourself.

Syntax: 

new_function = functools.partial(original_function, fixed_arguments)

Example:

Python

Output:

Fixing Arguments Using functools

Explanation: Here, we fixed the first argument x=3 using partial(). Now find(7) is like calling multiply (3,7). It simplifies the code whenever you want to create custom versions of existing functions.

Reducing a Sequence Using functools.reduce() in Python

If you have a list of numbers and you want to apply a function like addition or multiplication to combine them into a single value, reduce() will help you cover that. It does this by taking two items at a time and combining them, repeatedly.

Syntax: 

functools.reduce(function, iterable)

Example:

Python

Output:

Reducing a Sequence Using functools

Explanation: Here, the reduce() function starts by adding 6 + 7 = 13, then 13 + 8 = 21, and finally 21 + 9 = 30.

Get 100% Hike!

Master Most in Demand Skills Now!

Preserving Metadata by Implementing functools.wraps() in Python

It is important to preserve the original function’s name and documentation when you decide to create decorators. Decorators are functions that modify other functions. Without using wraps, important information like the name and docstring gets lost.

Syntax:

@functools.wraps(original_function)
def wrapper_function(arguments):
    # function body

Example:

Python

Output:

Preserving Metadata by Implementing functools

Explanation: Here, the original function name greet and its docstring “Says hello.” are preserved even after decoration, and this is the direct result of implementing @functools.wraps(func). Also, a decorator wraps another function, and that @wraps helps retain the identity of the wrapped function.

Comparison Table: Key Methods of functools in Python

Method Description Syntax Common Use
lru_cache() Caches the results of function calls to avoid recomputation, speeding up your code. @functools.lru_cache(maxsize=None) Speeding up repeated function calls by caching results.
partial() Creates a new function with some arguments fixed ahead of time. functools.partial(func, fixed_args) Simplifying complex function calls with preset arguments.
reduce() Reduces an iterable to a single accumulated value by applying a function cumulatively. functools.reduce(function, iterable) Folding a list into a single result, like sum or product.
wraps() Preserves the original function’s metadata when using decorators. @functools.wraps(func) Maintaining the original function’s name, docstring, and signature after decoration.

Making Function Overloading Easy Using functools.singledispatch

When you want a case where a single function is made to behave differently depending on the type of argument it receives, you usually have to write a lot of if-else statements. You can attempt to overload a function cleanly, without cluttering your code, using functools.singledispatch.

Syntax:

@functools.singledispatch
def function_name(arg):
    # default behaviour
@function_name.register(type)
def function_name_for_type(arg):
    # behaviour for the specific type

Example:

Python

Output:

Making Function Overloading Easy Using functools

Explanation: Here, you created a function called greet. This function behaves differently based on whether you pass a string or an integer, etc. This helps in making your program smart enough to react according to the input type automatically.

Advanced Features & Recent Enhancements

cached_property (Python 3.8+)

The cached_property decorator allows you to convert a class method into a property whose value is computed only once and then cached for future use. This is especially useful for expensive operations, such as database queries or complex calculations, where you don’t want to recompute the result every time. Instead, the result is stored and reused whenever you access the property again.

Improved lru_cache (Python 3.9+)
The lru_cache decorator has been enhanced to perform better in multi-threaded environments. It now ensures thread-safety, which makes it more reliable when used in concurrent applications. Another improvement is the option to set maxsize=None, which turns it into an unlimited cache, allowing you to store all function results without eviction.

cache (Python 3.9+)
A newer addition to the module is the cache decorator, which acts as a simplified version of lru_cache. Unlike lru_cache. It does not have a limit on stored items, making it a good choice when you want unlimited caching and don’t need the overhead of maintaining a least-recently-used strategy.

Enhanced singledispatchmethod (Python 3.8+)
The singledispatchmethod decorator brings the power of single dispatch to class methods. This feature allows you to overload methods in a clean and organized way, dispatching them based on the type of the first argument. It improves code readability and helps you manage multiple method variations within a class.

Better partial Function (Recent Updates)
The partial function, which allows you to pre-fill arguments of a function, has been optimized to work more efficiently and integrates better with type annotations. This makes it easier to write reusable and maintainable functions, while also keeping your codebase cleaner and more readable.

Metadata Preservation with @wraps
The @wraps decorator has been refined to ensure that when you wrap a function with a custom decorator, the original function’s metadata, such as its name, docstring, and annotations, are not lost. This enhancement is important for debugging, documentation, and keeping your code transparent and developer-friendly.

Use Cases for functools in Python

Use case 1: Simplify rich comparisons in classes using functools.total_ordering

When you make custom classes, you often need to define many comparison methods like <, <=, >, >=. Writing all of them manually would be hard. functools.total_ordering solves this by allowing you to define just one or two methods, and it will create the rest for you. This saves you a lot of time when building classes that need full comparison features.

Syntax: 

@functools.total_ordering
class ClassName:
    def __eq__( self , other):
        # Define equality logic
    def __lt__( self , other):
        # Define less-than logic

Example:

Python

Output:

Use Cases for functools in Python

Explanation: Here, the @functools.total_ordering decorator automatically creates the missing comparison methods (le, gt, and ge) based on the ones you define (eq and lt), so you can use all comparison operators without having to write them yourself.

Use case 2: Simple way to cache without limits

Syntax:

@functools.cache
def function_name(parameters):
    # function body

Example:

Python

Output:

Use Case 2 for functools in Python

Explanation: Here, the first call computes 12*11, but the second call doesn’t; It retrieves the answer from the cache. functools.cache makes it even easier than lru_cache, if you don’t care about memory limits and just want maximum simplicity.

Best Practices for Using functools in Python

Follow these simple tips to make your work with functools smooth and efficient:

  • Always use @wraps when writing decorators to preserve the original function’s name and documentation.
  • Use lru_cache() only when your function depends purely on its inputs and has no external side effects.
  • Always set a reasonable maxsize when caching to avoid memory overflows.
  • Choose partial to make APIs simpler and avoid writing repetitive wrapper functions manually.
  • Use reduce only when necessary; Sometimes, a loop can be more readable for beginners.
Free Python Course for Beginners
Code at your own pace and build real projects
quiz-icon

Conclusion

A module that makes your code smarter, faster in execution, and easier to work with is one of the qualities that make functools one of the most useful modules in Python programming. Does not matter if you are using caching to optimize performance, using partial to simplify call functions, using reduce to fold sequences, using wraps to preserve metadata; The functools module has it covered with a wide range of useful tools. Using this module will save your time, improve your overall code structure, and unlock potential you might not even be aware of.

To take your skills to the next level, enrol in our Python training course and gain hands-on experience. Also, prepare for job interviews using our Python interview questions, designed by industry experts.

FAQs on functools in Python

Q1. Is the functools a built-in module in Python?

Yes, it is part of Python’s standard library, and you don’t need to install it externally.

Q2. When should I use lru_cache?

Use it when you have a function whose output depends only on its input, and it is called many times with the same arguments.

Q3. Can I limit how many results lru_cache stores?

Yes, you can set a maxsize parameter like @lru_cache(maxsize=100).

Q4. Why use functools.partial()?

Use it when you want to create a version of a function with fewer parameters for easier use.

Q5. What happens if I don't use wraps in a decorator?

If you don’t use wraps, the decorated function loses its original name, docstring, and metadata which makes it difficult to debug.

Q6. What is the functools module used for in Python?

The functools module in Python is used to provide higher-order functions that help in functional programming, such as function manipulation and optimization.

Q7. How does lru_cache() improve Python performance?

The lru_cache() in Python improves performance by storing results of expensive function calls and reusing them when the same inputs occur again.

Q8. What’s the difference between cache() and lru_cache()?

The cache() stores unlimited results of function calls, while lru_cache() stores a limited number using a least-recently-used eviction policy.

Q9. When should I use functools.partial() vs writing a wrapper?

Use functools.partial() when you just need to fix some function arguments, but write a wrapper when you need more custom logic or processing.

Q10. How does @functools.wraps preserve function metadata?

@functools.wraps preserves function metadata by copying the original function’s name, docstring, and attributes to the wrapper function.

Q11. Can functools.singledispatch be used with annotations?

Yes, functools.singledispatch can work with type annotations by dispatching functions based on the annotated type of the first argument.

Q12. What is cached_property() and when should I use it?

cached_property() is used to cache the result of a property method after the first call, making it useful for expensive computations that don’t change.

About the Author

Senior Consultant Analytics & Data Science, Eli Lilly and Company

Sahil Mattoo, a Senior Software Engineer at Eli Lilly and Company, is an accomplished professional with 14 years of experience in languages such as Java, Python, and JavaScript. Sahil has a strong foundation in system architecture, database management, and API integration. 

EPGC Data Science Artificial Intelligence