Introduction
Hello everyone, I am Stubborn Bronze III. Welcome to follow me on my WeChat public account: Stubborn Bronze III. Please like, bookmark, and follow, a triple click!
Welcome to Day 87 of Python Practice!
Today we will learn about the <span>functools</span> module in the Python standard library.
In Python development, how can we elegantly handle common issues such as caching, function decoration, and sorting? The <span>functools</span> module in the Python standard library is an indispensable tool for you!
It provides a series of higher-order functions and callable object manipulation tools, making your code more concise and efficient.
Today, we will delve into the mysteries of the <span>functools</span> module, mastering these practical tools to elevate your Python skills!
1. Overview of the functools Module
<span>functools</span> module is a core toolkit in the Python standard library for handling higher-order functions and callable object operations.
Since its introduction in Python 2.5, this module has been continuously expanded, adding practical features such as <span>cache</span>, <span>cached_property</span>, and <span>singledispatch</span>.
It focuses on simplifying common scenarios such as function decoration, cache management, sorting, and polymorphism, making it a powerful tool for improving code quality and performance.
It is important to note that while <span>functools</span> is powerful, some commonly used tools like <span>namedtuple</span> actually belong to the <span>collections</span> module, reflecting the modular design of the Python standard library.
2. cache: Lightweight Function Caching
<span>cache</span> decorator is a simple caching tool introduced in Python 3.9, used to cache the return values of functions to avoid redundant calculations. Unlike <span>lru_cache</span>, it does not impose a size limit on the cache, making it suitable for lightweight scenarios.
from functools import cache
@cache
def factorial(n):
return n * factorial(n-1) if n else 1
print(factorial(10)) # Calculate and cache
print(factorial(5)) # Directly retrieve from cache
print(factorial(12)) # Partial calculation, partial cache
This code demonstrates the efficiency of <span>cache</span>: the first calculation of 10! will recursively compute 11 times, but subsequent calls to 5! will directly retrieve from the cache, while calculating 12! will only require the last two recursive calculations.
3. cached_property: Instance Attribute Caching
<span>cached_property</span> converts class methods into properties, caching the result after computation to avoid redundant calculations. It is suitable for high-cost attribute calculations.
import statistics
from functools import cached_property
class DataSet:
def __init__(self, data):
self._data = tuple(data)
@cached_property
def stdev(self):
print("Calculating standard deviation...")
return statistics.stdev(self._data)
data = DataSet([1, 2, 3, 4, 5])
print(data.stdev) # Calculate and cache
del data.stdev # Clear cache
print(data.stdev) # Recalculate
At runtime, the first access to <span>stdev</span> will print “Calculating standard deviation…”, and subsequent accesses will directly return the cached value. After deleting the attribute, accessing it again will recalculate, making it very suitable for scenarios requiring lazy computation.
4. cmp_to_key: Converting Old-Style Comparison Functions
<span>cmp_to_key</span> converts old-style comparison functions into <span>key</span> functions, used in contexts like <span>sorted()</span>, commonly used for compatibility with Python 2 code.
from functools import cmp_to_key
def compare(a, b):
if a < b:
return -1
elif a > b:
return 1
else:
return 0
sorted_list = sorted([3, 1, 2], key=cmp_to_key(compare))
print(sorted_list) # [1, 2, 3]
# Practical application: sorting by locale
import locale
sorted_words = sorted(["café", "cacao"], key=cmp_to_key(locale.strcoll))
print(sorted_words) # ['cacao', 'café'] (depends on system locale)
This example demonstrates how to adapt a custom comparison function to the modern Python sorting interface, particularly suitable for handling multilingual sorting scenarios.
5. lru_cache: LRU Cache with Size Limit
<span>lru_cache</span> provides an LRU (Least Recently Used) cache with a maximum size, suitable for scenarios where memory usage needs to be controlled.
from functools import lru_cache
import time
@lru_cache(maxsize=3)
def slow_function(x):
time.sleep(1)
return x * x
start = time.time()
print(slow_function(1)) # Takes 1 second
print(slow_function(2)) # Takes 1 second
print(slow_function(3)) # Takes 1 second
print(slow_function(1)) # Retrieved from cache (0 seconds)
print(slow_function(4)) # 1 second (cache full, remove 1)
print(slow_function.cache_info()) # CacheInfo(hits=1, misses=4, maxsize=3, currsize=3)
print(f"Total time: {time.time() - start:.2f} seconds")
This example illustrates the operation of the LRU cache: when the cache is full, the least recently used item will be removed. By using <span>cache_info()</span>, you can monitor the cache hit rate and optimize the <span>maxsize</span> parameter.
6. total_ordering: Simplifying Comparison Operators
<span>total_ordering</span> decorator simplifies class comparison logic by implementing one comparison method and automatically generating other comparison operators.
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
return self.score == other.score
def __lt__(self, other):
return self.score < other.score
s1 = Student("Alice", 85)
s2 = Student("Bob", 90)
print(s1 < s2) # True
print(s1 > s2) # False
print(s1 <= s2) # True
print(s1 >= s2) # False
By only implementing <span>__eq__</span> and <span>__lt__</span>, <span>total_ordering</span> automatically provides <span>></span>, <span><=</span>, and <span>>=</span> operators, significantly reducing the amount of code.
7. partial and partialmethod: Parameter Pre-setting
<span>partial</span> is used to create partially applied functions, while <span>partialmethod</span> is designed specifically for class methods.
from functools import partial, partialmethod
# Example of partial: fixing the base parameter
basetwo = partial(int, base=2)
print(basetwo('1010')) # 10
# Example of partialmethod: presetting parameters for class methods
class Cell:
def __init__(self):
self._alive = False
@property
def alive(self):
return self._alive
def set_state(self, state):
self._alive = state
set_alive = partialmethod(set_state, True)
set_dead = partialmethod(set_state, False)
c = Cell()
c.set_alive()
print(c.alive) # True
c.set_dead()
print(c.alive) # False
<span>partial</span> allows you to “pre-install” some parameters for a function, while <span>partialmethod</span> is specifically for class methods, automatically handling the <span>self</span> parameter, making class interfaces cleaner.
8. reduce: Efficient Data Aggregation
<span>reduce</span> applies a function cumulatively to the items of a sequence, reducing it to a single value, suitable for data aggregation scenarios.
from functools import reduce
# Calculate factorial
factorial = reduce(lambda x, y: x * y, range(1, 6))
print(factorial) # 120
# String concatenation
words = ["Hello", "World", "Python"]
sentence = reduce(lambda x, y: x + " " + y, words)
print(sentence) # "Hello World Python"
# Find the maximum value in a list
max_value = reduce(lambda a, b: a if a > b else b, [3, 1, 4, 1, 5, 9])
print(max_value) # 9
<span>reduce</span> is a core tool in functional programming, simplifying complex aggregation operations into a single line of code, but be cautious of readability; for complex scenarios, loops are recommended.
9. singledispatch and singledispatchmethod: Single Dispatch Polymorphism
<span>singledispatch</span> implements function dispatch based on parameter types, while <span>singledispatchmethod</span> is specifically for class methods.
from functools import singledispatch, singledispatchmethod
@singledispatch
def process(data):
print("Default processing")
@process.register(int)
def _(data):
print(f"Integer processing: {data}")
@process.register(str)
def _(data):
print(f"String processing: {data}")
@process.register(list)
def _(data):
print(f"List processing: {len(data)} elements")
process(42) # Integer processing: 42
process("hello") # String processing: hello
process([1,2,3]) # List processing: 3 elements
# Class method example
class Calculator:
@singledispatchmethod
def calculate(self, arg):
raise NotImplementedError
@calculate.register(int)
def _(self, arg):
return arg * 2
@calculate.register(str)
def _(self, arg):
return arg.upper()
calc = Calculator()
print(calc.calculate(10)) # 20
print(calc.calculate("hi")) # HI
This design makes the code more modular; adding new type handling only requires registering a new function without modifying the existing logic.
10. wraps and update_wrapper: Preserving Function Metadata
<span>wraps</span> decorator is used to preserve the metadata of the decorated function, such as its name and docstring.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Pre-operation")
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Example function documentation"""
print("Actual execution")
print(example.__name__) # example
print(example.__doc__) # Example function documentation
example()
If <span>wraps</span> is not used, <span>example.__name__</span> will become <span>wrapper</span>, and the docstring will be lost. <span>update_wrapper</span> is the underlying implementation, while <span>wraps</span> is a more concise decorator form.
11. get_cache_token: Cache State Monitoring
<span>get_cache_token</span> returns an integer that changes when the cache is cleared, used for monitoring cache state.
from functools import lru_cache, get_cache_token
@lru_cache(maxsize=10)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
token1 = get_cache_token()
fib(10)
fib.cache_clear()
token2 = get_cache_token()
print(token1 != token2) # True
# Practical application: checking if the cache has been reset
def check_cache_valid():
token = get_cache_token()
# Perform operations that may affect the cache...
return token == get_cache_token()
print(check_cache_valid()) # False (because cache_clear has been executed)
This tool is very useful for testing cache logic or monitoring cache state, ensuring that the caching mechanism works as expected.
Practical Tips for functools
- Cache Strategy Selection: Use
<span>cache</span>for lightweight scenarios, and<span>lru_cache</span>when memory usage needs to be controlled. - Attribute Caching:
<span>cached_property</span>is safer than manual caching, avoiding redundant calculations. - Comparison Operators:
<span>total_ordering</span>can reduce comparison code by 70%. - Decorator Chains:
<span>@wraps</span>should be the innermost in a decorator chain to ensure metadata is not lost. - Polymorphic Dispatch:
<span>singledispatch</span>makes the code more modular; adding new types only requires registering new functions.
Conclusion
<span>functools</span> module may seem simple, but it hides many secrets. From cache optimization to function dispatch, from property management to recursion protection, these tools can make your code both elegant and efficient. Mastering these techniques will allow you to navigate daily development with ease, writing more professional and robust Python code.
Remember: good tools are an extension of the programmer, and <span>functools</span> is an indispensable precision instrument in your Python toolbox.
Finally, thank you for reading! Please follow me on my WeChat public account: Stubborn Bronze III.Like, bookmark, and follow, a triple click!! Feel free to click the [👍 Like Author] button to donate💰💰, treat me to a cup of coffee☕️!
Day 86 of Python Practice: Say Goodbye to Dependency Chaos! Master the Official Python Tool venv, and project isolation will be worry-free.Day 85 of Python Practice: Master the re module from scratch—A complete guide to regular expressions.