
partial()
从概念上讲, partial是一种通过将部分参数应用于现有函数来创建新函数的便捷方法 。functools.partial
允许你创建一个新函数,该函数是现有函数的修改版本。你可以为一个或多个参数指定默认值,这意味着你可以使用比原始函数更少的参数来调用新函数。

from functools import partial
# Define a regular function
def func(arg1, arg2, arg3):
# Do something
pass
# Create a partial function by fixing some arguments
partial_func = partial(func, fixed_arg1, fixed_arg2)
# Now call it with the remaining arguments
result = partial_func(arg3)
示例1
from functools import partial
def calculate_bmi(height: float, weight: float) -> str:
bmi = weight / (height ** 2)
return f"Your Body Mass Index (BMI) is {bmi:.2f}."
# Set my height once and reuse the function
bmi_for_my_height = partial(calculate_bmi, height=1.75)
# Track weight changes over time
print(bmi_for_my_height(weight=63)) # 01/01/2024
# → Your Body Mass Index (BMI) is 20.57.
print(bmi_for_my_height(weight=66)) # 01/06/2024
# → Your Body Mass Index (BMI) is 21.55.
示例2:
from functools import partial
def weather_report(city: str, temp: int, rain: bool) -> str:
return f"""Temperature in {city} is {temp}ºC.
It is {'raining' if rain else 'not raining'}."""
# Regular usage
print(weather_report('Lima', 17, True))
# → Temperature in Lima is 17ºC.
# → It is raining.
# Lock in the city as 'Madrid'
madrid_weather_report = partial(weather_report, city="Madrid")
# Now just update the weather
print(madrid_weather_report(temp=24, rain=False))
# → Temperature in Madrid is 24ºC.
# → It is not raining.
将 partial()
与内置函数一起使用
from functools import partial
# Create a version of print with a custom separator
print_with_sep = partial(print, sep="\n")
# Now this prints each item on a new line
print_with_sep("Name: Jaume", "Age: 25", "Pet: Mimi")
# → Name: Jaume
# → Age: 25
# → Pet: Mimi
无需包装器,无需额外函数——只需一行代码,即可完成。这种调整可立即让你的代码更简洁、更易读。
Partial Functions vs. Lambda
Partial Functions:作用是为现有函数预填充参数。语法 : partial(existing_func, fixed_args)
与现有功能绑定——保持代码结构化和可读性。非常适合重复使用和简化现有功能。更明确、更易于阅读,尤其是在较大的项目中。
from functools import partial
def power(base, exponent):
return base ** exponent
# Regular function call for squaring
print(power(5, 2))
# → 25
# Using partial to create a new function for squaring
square = partial(power, exponent=2)
print(square(5))
# → 25
Lambda Functions: 创建匿名内联函数。语法 : lambda args: expression
超轻且灵活,可实现快速、一次性功能。非常适合短期、临时的操作。当函数变得复杂时,可能会变得混乱且难以阅读。
# Using a lambda function to square a number
square = lambda base: base ** 2
print(square(5))
# → 25
partial()
:当我想重用现有函数并固定某些参数时,我会使用它,尤其是当可读性和结构很重要时。lambda
:当我不需要完整的函数定义时,我会使用 lambda 来完成快速的内联任务。
Reduce
functools.reduce
将两个参数的函数从左到右累积应用于可迭代对象中的项,从而将可迭代对象减少为单个值。
from functools import reduce
def add(x, y):
return x + y
# Sum the elements of the list [1, 2, 3, 4]
result = reduce(add, [1, 2, 3, 4])
print(result) # Output: 10
lru_cache
functools.lru_cache
是一个实现最近最少使用(LRU: Least Recently Used cache)缓存的装饰器。它可以用于缓存函数的结果,以便后续使用相同参数的调用可以更快地返回结果。
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(10)) # Output: 55
from functools import lru_cache
import time
@lru_cache(maxsize=5) # Store up to 5 recent results
def slow_function(n):
time.sleep(2) # Simulate a time-consuming computation
return n * n
# First call (takes time)
# 第一次呼叫(需要时间)
print(slow_function(4)) # Output: 16 (after 2 seconds)
# Second call (instant) # 第二次呼叫(即时)
print(slow_function(4)) # Output: 16 (retrieved from cache)
cmp_to_key
functools.cmp_to_key
函数用于将比较函数转换为键函数。它可以用来通过自定义比较函数对列表进行排序。
from functools import cmp_to_key
def compare(x, y):
if x < y:
return -1
elif x > y:
return 1
else:
return 0
# Sort the list [3, 1, 2] using the comparison function
sorted_list = sorted([3, 1, 2], key=cmp_to_key(compare))
print(sorted_list) # Output: [1, 2, 3]
扩展自定义排序以处理更复杂的数据结构,例如按多个键对字典列表进行排序。
from functools import cmp_to_key
# Comparison function for multi-key sorting
def compare_multi_keys(a, b):
if a['age'] != b['age']:
return a['age'] - b['age']
return (a['name'] > b['name']) - (a['name'] < b['name'])
# Sample data
data = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 24},
{'name': 'Charlie', 'age': 25},
{'name': 'Dave', 'age': 35}
]
# Sorting using cmp_to_key
sorted_data = sorted(data, key=cmp_to_key(compare_multi_keys))
print(sorted_data)
singledispatch
functools.singledispatch
是一个函数装饰器,它实现了单次调度(single dispatch),这是一种根据单个参数的类型选择要调用的函数的机制。它类似于内置的 dispatchmethod
装饰器,但它作用于普通函数而非方法。它将一个函数转换为一个通用函数 ——该函数可以根据第一个参数的类型有不同的实现。
from functools import singledispatch
@singledispatch
def func(arg):
print("Unknown type")
@func.register(int)
def _(arg):
print("Got an int")
@func.register(str)
def _(arg):
print("Got a string")
func(5) # Output: "Got an int"
func("hello") # Output: "Got a string"
func([1, 2, 3]) # Output: "Unknown type"
在 @serialize.register(<type>)
上,每当你用这种类型调用 serialize()
时,都使用这个版本的函数。基本函数( serialize
)是默认的处理程序 。每个 .register(type)
函数都是一个专门的版本 。仅考虑第一个参数的类型 。这就是为什么它被称为单次分派。
from functools import singledispatch
import json
@singledispatch
def serialize(value):
raise NotImplementedError(f"Cannot serialize object of type {type(value)}")
@serialize.register(str)
def _(value):
return value
@serialize.register(dict)
def _(value):
return json.dumps(value)
@serialize.register(list)
def _(value):
return ','.join(map(str, value))
# Example usage
print(serialize("hello")) # hello
print(serialize({"a": 1})) # {"a": 1}
print(serialize([1, 2, 3])) # 1,2,3
total_ordering
functools.total_ordering
是一个类装饰器,它基于单个比较方法为类生成所有丰富的比较方法(例如 __lt__
、 __le__
等)。定义自定义类时,实现所有比较方法( <, >, <=, >=, ==, !=
)可能非常繁琐。total_ordering 装饰 total_ordering
会为您生成缺少的比较方法,只需要 __eq__
和另一个比较方法即可。
from functools import total_ordering
@total_ordering
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
def __lt__(self, other):
return self.age < other.age
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
person3 = Person("Charlie", 30)
print(person1 > person2) # Output: True
print(person1 < person3) # Output: False
print(person1 == person3) # Output: True
wraps
在 Python 中创建装饰器时,它们可能会遮挡原始函数的元数据(例如其名称和文档字符串)。functools 中的 wraps
装饰 functools
可以保留这些信息。
from functools import wraps
def log_function(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_function
def greet(name):
"""Says hello to the given name."""
return f"Hello, {name}!"
print(greet("Sam"))
print(greet.__name__) # Output: greet (instead of wrapper)
print(greet.__doc__) # Output: Says hello to the given name.
update_wrapper wrapper
functools.update_wrapper
函数用于更新包装函数,使其看起来像被包装函数。它通常与 functools.wrap
装饰器结合使用,后者是用于装饰包装函数的装饰器,用于确保包装函数保留被包装函数的文档字符串、名称和其他属性。
如何使用 update_wrapper
和 wrap
创建一个简单的装饰器,为函数名称添加前缀:
import functools
def prefix_decorator(prefix):
def decorator(func):
@functools.wrap
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper.__name__ = f"{prefix}_{func.__name__}"
return wrapper
return decorator
@prefix_decorator("test")
def add(x, y):
"""Add two numbers together"""
return x + y
print(add.__name__) # Output: "test_add"
print(add.__doc__) # Output: "Add two numbers together"
update_wrapper
函数也可以用于直接更新包装器函数的属性,而无需使用 wrap
装饰器。以下是示例:
import functools
def prefix_decorator(prefix):
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
functools.update_wrapper(wrapper, func)
wrapper.__name__ = f"{prefix}_{func.__name__}"
return wrapper
return decorator
@prefix_decorator("test")
def add(x, y):
"""Add two numbers together"""
return x + y
print(add.__name__) # Output: "test_add"
print(add.__doc__) # Output: "Add two numbers together"