python 高阶函数和 functools 模块

Python 高阶函数和 functools 模块

Python 中的高阶函数和 functools 模块是函数式编程的重要组成部分。

目录

  1. 高阶函数基础
  2. functools模块概述
  3. functools.reduce()
  4. 偏函数 functools.partial()
  5. @functools.wraps()装饰器
  6. 高阶函数、闭包与装饰器的关系
  7. 实际应用案例

高阶函数基础

高阶函数是指接受一个或多个函数作为参数,或者返回一个函数的函数。在Python中,函数是一等公民,可以像其他对象一样被传递和使用。

# 定义一个简单的高阶函数
def apply_func(func, value):
    """接收一个函数和一个值,返回函数处理后的结果"""
    return func(value)

# 示例函数
def square(x):
    return x * x

def double(x):
    return x * 2

# 使用高阶函数
result1 = apply_func(square, 4)  # 返回16
result2 = apply_func(double, 5)  # 返回10

print(f"square(4) = {result1}")
print(f"double(5) = {result2}")

# 使用lambda表达式
result3 = apply_func(lambda x: x + 10, 5)  # 返回15
print(f"x + 10 for x=5: {result3}")

# 返回函数的高阶函数
def create_multiplier(factor):
    """返回一个将输入值乘以factor的函数"""
    def multiplier(x):
        return x * factor
    return multiplier

# 创建特定的乘法函数
triple = create_multiplier(3)
print(f"triple(4) = {triple(4)}")  # 返回12

Python内置的高阶函数包括map(), filter(), sorted()等。

functools模块概述

functools模块提供了一系列用于操作可调用对象的高阶函数。这些工具特别适合函数式编程风格的开发。

主要功能包括:

  • reduce(): 将二元函数累积应用到序列的项目
  • partial(): 创建偏函数,冻结函数部分参数
  • wraps(): 用于装饰器,保留被装饰函数的元信息
  • lru_cache(): 提供缓存功能的装饰器
  • total_ordering(): 自动生成比较方法的类装饰器
  • singledispatch(): 基于类型的单分派泛型函数

functools.reduce()

functools.reduce()函数将一个二元函数(接受两个参数的函数)从左到右依次应用于序列的项,以将该序列归约为单个值。

from functools import reduce

# 计算列表中所有数字的总和
numbers = [1, 2, 3, 4, 5]
sum_result = reduce(lambda x, y: x + y, numbers)
print(f"Sum of {numbers} = {sum_result}")  # 输出: 15

# 计算阶乘
def multiply(x, y):
    return x * y

factorial = reduce(multiply, range(1, 6))  # 5!
print(f"5! = {factorial}")  # 输出: 120

# 提供初始值
# 从10开始,依次减去列表中的每个数
subtraction = reduce(lambda x, y: x - y, [1, 2, 3], 10)
print(f"10-1-2-3 = {subtraction}")  # 输出: 4

# 使用reduce实现字符串连接
words = ['Hello', ' ', 'world', '!']
sentence = reduce(lambda x, y: x + y, words)
print(sentence)  # 输出: Hello world!

# 找出最大值
max_value = reduce(lambda x, y: x if x > y else y, numbers)
print(f"Max value in {numbers} is {max_value}")  # 输出: 5

偏函数 functools.partial()

偏函数允许我们通过固定函数的某些参数来创建一个新函数。这在需要多次调用同一个函数但只有部分参数变化时很有用。

from functools import partial

# 基础函数
def power(base, exponent):
    return base ** exponent

# 创建平方函数 - 固定指数为2
square = partial(power, exponent=2)
print(f"square(3) = {square(3)}")  # 输出: 9
print(f"square(4) = {square(4)}")  # 输出: 16

# 创建立方函数 - 固定指数为3
cube = partial(power, exponent=3)
print(f"cube(3) = {cube(3)}")  # 输出: 27

# 固定基数的函数
power_of_two = partial(power, 2)
print(f"2^3 = {power_of_two(3)}")  # 输出: 8
print(f"2^4 = {power_of_two(4)}")  # 输出: 16

# 在内置函数上使用partial
# 创建一个总是将文本转换为十六进制的函数
hex_converter = partial(int, base=16)
print(hex_converter('a'))  # 输出: 10
print(hex_converter('f'))  # 输出: 15
print(hex_converter('ff'))  # 输出: 255

# 使用partial预设函数参数
def greet(greeting, name):
    return f"{greeting}, {name}!"

# 创建特定问候语的函数
hello = partial(greet, "Hello")
goodbye = partial(greet, "Goodbye")

print(hello("Alice"))  # 输出: Hello, Alice!
print(goodbye("Bob"))  # 输出: Goodbye, Bob!

@functools.wraps()装饰器

@functools.wraps()用于装饰器中,确保被装饰的函数保留其原始信息(如函数名、文档字符串等)。没有它,装饰后的函数会丢失元数据。

from functools import wraps
import time

# 不使用wraps的装饰器
def simple_timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Function {func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

# 使用wraps的装饰器
def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Function {func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

# 使用两种装饰器
@simple_timer
def function_without_wraps():
    """这是一个测试函数的文档字符串."""
    time.sleep(0.5)
    return "Function completed"

@timer
def function_with_wraps():
    """这是一个测试函数的文档字符串."""
    time.sleep(0.5)
    return "Function completed"

# 查看函数名和文档
print("不使用wraps:")
print(f"函数名: {function_without_wraps.__name__}")
print(f"文档: {function_without_wraps.__doc__}")

print("\n使用wraps:")
print(f"函数名: {function_with_wraps.__name__}")
print(f"文档: {function_with_wraps.__doc__}")

# 调用函数
function_without_wraps()
function_with_wraps()

运行这段代码,你会看到没有使用@wraps的装饰器会导致函数名变为wrapper,并且丢失原始文档字符串。

高阶函数、闭包与装饰器的关系

这三个概念是密切相关的:

  1. 高阶函数是可以接受函数作为参数或返回函数的函数。

  2. 闭包是一个内部函数,它可以访问其外部函数的变量,即使外部函数已经执行完毕。闭包是高阶函数返回函数的一种实现方式。

  3. 装饰器是一种特殊的高阶函数,它接受一个函数作为参数并返回一个新函数,通常用于在不修改原始函数代码的情况下扩展其功能。

# 闭包示例
def create_counter(start=0):
    """创建一个简单的计数器函数"""
    count = [start]  # 使用列表是为了在内部函数中修改值
    
    def counter():
        count[0] += 1
        return count[0]
    
    return counter  # 返回内部函数,形成闭包

# 装饰器示例
from functools import wraps

def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned: {result}")
        return result
    return wrapper

# 使用装饰器
@log_calls
def add(x, y):
    """加法函数"""
    return x + y

# 测试闭包
counter1 = create_counter()
counter2 = create_counter(10)

print(counter1())  # 1
print(counter1())  # 2
print(counter2())  # 11

# 测试装饰器
result = add(3, 5)
print(f"Final result: {result}")

# 使用闭包和装饰器创建参数化装饰器
def repeat(times):
    """一个参数化的装饰器,重复执行函数指定次数"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    return f"Hello, {name}!"

print(greet("World"))  # 将输出包含3个问候的列表

实际应用案例

让我们看看这些概念在实际项目中的应用:

from functools import wraps, partial, reduce, lru_cache
import time
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 使用wraps的装饰器来添加重试逻辑
def retry(max_attempts=3, delay=1):
    """创建一个重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts == max_attempts:
                        logger.error(f"Function {func.__name__} failed after {max_attempts} attempts. Error: {e}")
                        raise
                    logger.warning(f"Attempt {attempts} failed. Retrying in {delay} seconds...")
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

# 使用lru_cache进行缓存优化
@lru_cache(maxsize=128)
def fibonacci(n):
    """计算斐波那契数列的第n个数"""
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# 使用partial定制HTTP请求函数
def make_request(method, url, **kwargs):
    """模拟HTTP请求"""
    logger.info(f"Making {method} request to {url} with args: {kwargs}")
    # 在实际应用中,这里会使用requests库
    return f"Response from {method} {url}"

# 创建特定类型的请求函数
get_request = partial(make_request, "GET")
post_request = partial(make_request, "POST")

# 使用自定义装饰器和reduce
@retry(max_attempts=3)
def fetch_and_process_data(urls):
    """获取并处理多个URL的数据"""
    # 模拟获取数据
    raw_data = [get_request(url) for url in urls]
    
    # 使用reduce处理数据
    combined_data = reduce(lambda x, y: x + "\n" + y, raw_data)
    return f"Processed data: {combined_data}"

# 测试应用
if __name__ == "__main__":
    # 测试fibonacci缓存
    start = time.time()
    result = fibonacci(35)
    end = time.time()
    print(f"Fibonacci(35) = {result}, took {end-start:.4f} seconds")
    
    # 查看缓存信息
    print(f"Fibonacci cache info: {fibonacci.cache_info()}")
    
    # 测试偏函数
    api_url = "https://api.example.com/"
    get_users = partial(get_request, api_url + "users")
    get_products = partial(get_request, api_url + "products")
    
    print(get_users(param="active"))
    print(get_products(id=123))
    
    # 测试重试装饰器和reduce
    try:
        result = fetch_and_process_data([
            "https://example.com/api/data1",
            "https://example.com/api/data2"
        ])
        print(result)
    except Exception as e:
        print(f"Operation failed: {e}")

总结

  1. 高阶函数让我们能够将函数作为参数传递或作为返回值,提供了极大的灵活性。

  2. functools.reduce() 允许将二元函数累积应用到序列的元素上,将序列归约为单一值。

  3. functools.partial() 通过固定部分参数创建新函数,简化了代码并提高可读性。

  4. @functools.wraps() 帮助我们在创建装饰器时保留被装饰函数的元信息。

  5. 闭包、装饰器和高阶函数相互关联,共同构成了Python函数式编程的基础。

你可能感兴趣的:(python,python,开发语言)