Image by Author | Ideogram
Python’s standard library has several utilities that can transform your code from clunky and verbose to elegant and efficient. Among these, the functools and itertools modules often come in super handy for non-trivial tasks.
Today, we’ll look at seven essential tools — functions and decorators — from these modules that’ll make your Python code better.
Let’s get started.
🔗 Link to the code on GitHub
1. functools.lru_cache
You can use the @lru_cache
decorator to cache function results, and to avoid repeating expensive operations.
Here’s an example:
from functools import lru_cache
@lru_cache(maxsize=128)
def fetch_user_data(user_id):
# Expensive database call
return database.get_user(user_id)
# First call hits database, subsequent calls use cache
user = fetch_user_data(123) # Database call
user = fetch_user_data(123) # Returns cached result
How it works: The @lru_cache
decorator stores results in memory. When fetch_user_data(123)
is called again, it returns the cached result instead of hitting the database. maxsize=128
keeps the 128 most recent results.
2. itertools.chain
To process multiple iterables as one continuous stream, you can use chain.from_iterable()
from the itertools module.
Let’s take an example:
from itertools import chain
# Process multiple log files as one stream
error_logs = ['app.log', 'db.log', 'api.log']
all_lines = chain.from_iterable(open(f) for f in error_logs)
error_count = sum(1 for line in all_lines if 'ERROR' in line)
How it works: chain.from_iterable()
takes multiple iterables and creates one continuous stream. It reads one line at a time.
3. functools.partial
Partial functions in Python are super helpful when you need to create specialized versions of functions. Meaning you’d like to create versions of the function with some arguments already set using partial
from the functools module.
Here’s an example of a partial function:
from functools import partial
import logging
def log_event(level, component, message):
logging.log(level, f"[{component}] {message}")
# Create specialized loggers
auth_error = partial(log_event, logging.ERROR, 'AUTH')
db_info = partial(log_event, logging.INFO, 'DATABASE')
# Clean usage
auth_error("Login failed for user")
db_info("Connection established")
How it works: partial
creates a new function with some arguments pre-filled. In the example, auth_error
is essentially log_event
with level and component already set, so you only need to provide the message.
4. itertools.combinations
When you need to generate all possible combinations of items for testing or optimization, you can use combinations
from the itertools module.
Consider the following example:
from itertools import combinations
features = ['cache', 'compression', 'cdn']
# Test all pairs of features
for combo in combinations(features, 2):
performance = test_feature_combo(combo)
print(f"{combo}: {performance}ms")
How it works: combinations(features, 2)
generates all possible pairs from the list. It creates combinations on-demand without storing them all in memory, making it efficient for large datasets.
5. functools.singledispatch
The @singledispatch
decorator from the functools module can help you make functions that act differently based on input type.
Look at the following code snippet:
from functools import singledispatch
from datetime import datetime
@singledispatch
def format_data(value):
return str(value) # Default
@format_data.register(datetime)
def _(value):
return value.strftime("%Y-%m-%d")
@format_data.register(list)
def _(value):
return ", ".join(str(item) for item in value)
# Automatically picks the right formatter
print(format_data(datetime.now())) # this outputs "2025-06-27"
print(format_data([1, 2, 3])) # this outputs "1, 2, 3"
How it works: Python checks the type of the first argument and calls the appropriate registered function. However, it uses the default @singledispatch
function if no specific handler exists.
6. itertools.groupby
You can group consecutive elements that share the same property using the groupby
function from itertools.
Consider this example:
from itertools import groupby
transactions = [
{'type': 'credit', 'amount': 100},
{'type': 'credit', 'amount': 50},
{'type': 'debit', 'amount': 75},
{'type': 'debit', 'amount': 25}
]
# Group by transaction type
for trans_type, group in groupby(transactions, key=lambda x: x['type']):
total = sum(item['amount'] for item in group)
print(f"{trans_type}: ${total}")
How it works: groupby
groups consecutive items with the same key. It returns pairs of (key, group_iterator)
. Important: it only groups adjacent items, so sort your data first if needed.
7. functools.reduce
You can use the reduce
function from the functools module to apply a function cumulatively to all elements in an iterable to get a single value.
Take the following example:
from functools import reduce
# Calculate compound interest
monthly_rates = [1.01, 1.02, 0.99, 1.015] # Monthly growth rates
final_amount = reduce(lambda total, rate: total * rate, monthly_rates, 1000)
print(f"Final amount: ${final_amount:.2f}")
How it works: reduce
takes a function and applies it step by step: first to the initial value (1000) and the first rate, then to that result and the second rate, and so on. It works well for operations that build up state.
Wrapping Up
To sum up, we’ve seen how you can use:
@lru_cache
when you have functions that are called often with the same argumentsitertools.chain
when you need to process multiple data sources as one continuous streamfunctools.partial
to create specialized versions of generic functionsitertools.combinations
for systematic exploration of possibilities@singledispatch
when you need type-based function behaviorgroupby
for efficient consecutive grouping operationsreduce
for complex aggregations that build up state
The next time you find yourself writing verbose loops or repetitive code, pause and consider whether one of these might provide a more elegant solution.
These are just a handful of tools I find helpful. There are many more if you take a closer look at the Python standard library. So yeah, happy exploring!
Bala Priya C is a developer and technical writer from India. She likes working at the intersection of math, programming, data science, and content creation. Her areas of interest and expertise include DevOps, data science, and natural language processing. She enjoys reading, writing, coding, and coffee! Currently, she’s working on learning and sharing her knowledge with the developer community by authoring tutorials, how-to guides, opinion pieces, and more. Bala also creates engaging resource overviews and coding tutorials.