Python 轻松应对复杂编程 functools模块速查表

2024-03-05 22:20

本文主要是介绍Python 轻松应对复杂编程 functools模块速查表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


文章目录

      • 文章开篇
      • functools简介
      • reduce 二元归约
        • 1.计算列表中所有元素的和
        • 2.计算列表中所有元素的积
        • 3.连接列表中所有的字符串
        • 4.使用初始值进行累积操作
      • partial 偏函数
      • partialmethod 偏类实例
      • cmp_to_key 转换函数
      • cached_property 缓存实例
      • lru_cache 通过缓存增加代码性能
      • total_ordering 补全比较方法
      • singledispatch 分发泛型函数
        • 1.单分发机制
        • 2.自定义多重分发机制
      • wraps 保留元数据
      • update_wrapper 更新元数据
      • 总结


文章开篇

Python的魅力,犹如星河璀璨,无尽无边;人生苦短、我用Python!


functools简介

functools是Python标准库的一部分,专为高阶函数设计;
高阶函数能操作或返回函数,而functools模块则可将任何可调用对象当作函数处理
functools 模块常用的函数和装饰器:
函数

  • reduce 二元归约
  • partial 偏函数
  • partialmethod 偏类实例
  • cmp_to_key 转换方法

装饰器

  • cached_property缓存示例
  • lru_cache 通过缓存增加代码性能
  • singledispatch 分发泛型函数
  • total_ordering 补全比较方法
  • wraps 保留元数据
  • update_wrapper 更新元数据

reduce 二元归约

用于对一个序列进行归约操作,即对序列中的元素进行累积的二元操作
reduce函数接受一个二元函数(即接受两个参数的函数)和一个序列,然后对序列中的元素逐个进行二元函数操作,每次操作的结果都会作为下一次操作的第一个参数,直到序列中的所有元素都被处理完毕。

原型:reduce(function, iterable[, initializer])

  • **function:**含有两个参数的函数,reduce会把这个函数应用到序列的每一个元素上,进行累积操作。
  • **iterable:**可迭代对象,如列表、元组等;reduce会对这个可迭代对象中的元素进行累积操作。
  • **initializer(可选):**累积操作的初始值。如果提供了这个参数,那么累积操作会从这个初始值开始,而不是从序列的第一个元素开始。

1.计算列表中所有元素的和
from functools import reducenumbers = [1, 2, 3, 4, 5]
sum_numbers = reduce(lambda x, y: x + y, numbers)
print(sum_numbers)  # 输出 15

2.计算列表中所有元素的积
from functools import reducenumbers = [1, 2, 3, 4, 5]
product_numbers = reduce(lambda x, y: x * y, numbers)
print(product_numbers)  # 输出 120

3.连接列表中所有的字符串
from functools import reducestrings = ['Hello', ' ', 'world', '!']
concatenated_string = reduce(lambda x, y: x + y, strings)
print(concatenated_string)  # 输出 'Hello world!'

4.使用初始值进行累积操作
from functools import reducenumbers = [1, 2, 3, 4, 5]
product_of_numbers = reduce(lambda x, y: x * y, numbers, 10)
print(product_of_numbers)  # 输出:1200,这里的初始值是10,所以最终的结果是10 * 1 * 2 * 3 * 4 * 5 = 1200

partial 偏函数

偏函数是一种可以将原始函数中的参数“固定”或“冻结”的手段,从而创建一个新的函数,在调用时会使用的“固定”或“冻结”参数值,并且可以继续接受剩余的参数进行调用原始函数。
由 partial() 函数创建的对象有三个只读属性

  • func: 是一个可调用对象或者函数,调用 partial 对象时,会将其参数转发给 func;
  • args: 是调用 partial 时传入的参数,它将会被传递给 func;
  • keywords: 是调用 partial 时传入的关键字参数,它将被传入 func。
from functools import partial# 定义一个普通的函数
def greet(name, greeting, punctuation="!"):return f"{greeting}, {name}{punctuation}"# 使用 functools.partial 创建一个新的函数,其中 'greeting' 参数被预设为 'Hello'
hello_func = partial(greet, greeting='Hello')# 使用新函数,只需要提供 'name' 参数
print(hello_func('Alice'))  # 输出: "Hello, Alice!"# 还可以继续为 hello_func 提供额外的参数
print(hello_func('Bob', punctuation="?"))  # 输出: "Hello, Bob?"# 使用 functools.partial 创建一个新的函数,其中 'name' 和 'punctuation' 参数都被预设
hello_alice = partial(greet, name='Alice', punctuation="?")# 使用新函数,只需要提供 'greeting' 参数
print(hello_alice('Hi'))  # 输出: "Hi, Alice?"

partialmethod 偏类实例

functools.partialmethod 是 Python 标准库中 functools 模块提供的一个较少使用的装饰器;
它允许你创建一个**“部分类实例方法”(partial method);**
一个部分方法类似于一个偏函数(partial function),它是绑定到类实例上的方法,可以预先设置一些参数。
然而,与偏函数不同,部分方法只会在它们被调用时存在,如果没有提供必要的参数,它们可以表现为不存在。


from functools import partialmethodclass Calculator:def __init__(self, number):self.number = number# 使用 partialmethod 预设一个参数add_five = partialmethod(add, 5)# 这是一个普通的方法,用于加法def add(self, other):return self.number + other# 创建 Calculator 实例
calc = Calculator(10)# 调用预设了参数的方法
print(calc.add_five())  # 输出: 15# 调用普通方法,并提供额外的参数
print(calc.add(3))  # 输出: 13# 尝试调用没有预设参数的方法,将抛出 AttributeError
# print(calc.add_three())  # 抛出 AttributeError: 'Calculator' object has no attribute 'add_three'# 你可以动态地添加部分方法
def add_three(self, other=3):return self.add(other)Calculator.add_three = partialmethod(add_three)# 现在可以调用新添加的部分方法
print(calc.add_three())  # 输出: 13

cmp_to_key 转换函数

用于将传统的比较函数(cmp 函数)转换为键函数(key function)
帮助开发者在转换旧代码时保持一致性
从而使其能够与Python的内建排序函数(如 sorted() 和列表的 sort() 方法)一起使用;

比较函数

  • 比较函数接受两个参数,并根据比较的结果返回一个结果(负数、零或正数);
  • 负数表示第一个参数小于第二个参数,零表示它们相等,正数表示第一个参数大于第二个参数;
  • 这种函数通常在Python 2中使用,而在Python 3中,这种传统的比较函数已经被弃用;

键函数

  • 键函数接受一个参数,并返回一个用于排序的键。
  • 这个键通常是一个可以比较的对象(如数字、字符串等);
  • 排序函数会根据这个键来对原始数据进行排序;
  • 键函数是Python 3中推荐的方式来自定义排序逻辑;

应用场景

  • 旧代码迁移
  • 自定义排序
import functools# 定义比较函数
def cmp_func(x, y):if x < y:return -1elif x > y:return 1else:return 0# 使用 cmp_to_key 将比较函数转换为键函数
key_func = functools.cmp_to_key(cmp_func)# 定义一个列表
items = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]# 使用键函数对列表进行排序
sorted_items = sorted(items, key=key_func)print(sorted_items)  # 输出: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

cached_property 缓存实例

用于将类的实例方法转换为属性,并且这个属性的值只会被计算一次然后被缓存。
当你再次访问这个属性时,它会直接从缓存中返回之前计算的结果,而不是重新执行方法;

from functools import cached_property
import timeclass ExpensiveCalculation:def __init__(self, data):self.data = dataself._calculated_result = Noneself._calculation_time = None@cached_propertydef calculated_result(self):"""假设这是一个非常耗时的计算"""start_time = time.time()print("计算开始...")time.sleep(5)  # 假设这是一个耗时的计算print("计算结束...")self._calculated_result = sum(self.data)  # 假设这是计算的结果self._calculation_time = time.time() - start_timeprint(f"执行耗时:{self._calculation_time:.2f} 秒")return self._calculated_result# 使用示例
calc = ExpensiveCalculation([1, 2, 3, 4, 5])# 首次访问 calculated_result,会进行计算并打印耗时
print("第一次执行结果:", calc.calculated_result)
# 第一次调用,控制台打印如下:
# 计算开始...
# 计算结束...
# 执行耗时:5.00 秒
# 第一次执行结果: 15# 等待一段时间,以便更清楚地看到时间差异
time.sleep(2)# 再次访问 calculated_result,将直接从缓存中获取结果,不会重新计算,因此不会打印 "计算开始和计算结束等代码"
start_time = time.time()
print("第二次执行结果:", calc.calculated_result)
# 第二次调用,控制台打印如下:
# 第二次执行结果: 15
# 执行耗时:0.00 秒
end_time = time.time()
print(f"执行耗时:{end_time - start_time:.2f} 秒")

想象一下,你有一个大型数据集,为了分析它,你实现了一个保存整个数据集的类。
此外,你还实现了一些函数来计算诸如手头数据集的标准偏差之类的信息。
问题:你每次调用该方法时,它都会重新计算标准偏差—这需要时间啊!
这就是@cached_property派上用场的地方了


lru_cache 通过缓存增加代码性能

用于实现最近最少使用(Least Recently Used, LRU)缓存策略的装饰器
它可以将函数的结果缓存起来,以便在下次使用相同的参数调用函数时能够直接从缓存中取出结果,而不是重新计算。
这种机制可以有效地提高函数的执行效率,特别是对于那些计算代价较高、且经常需要重复计算同一组输入结果的函数来说,使用 LRU 缓存可以显著减少计算时间。
LRU 缓存策略是一种常用的缓存替换策略,它总是淘汰最长时间未被使用的缓存项。当缓存达到其容量上限时,最近最少使用的缓存项会被最先淘汰。

from functools import lru_cache
import time@lru_cache(maxsize=128)
def fibonacci(n):"""计算斐波那契数列的第n项"""if n <= 1:return nreturn fibonacci(n-1) + fibonacci(n-2)# 第一次计算斐波那契数列的第30项,并记录时间
start_time = time.time()
result = fibonacci(30)
print(f"Fibonacci(30) = {result}")
print(f"Time taken: {time.time() - start_time} seconds")# 第二次计算斐波那契数列的第30项,由于缓存,这次应该更快
start_time = time.time()
result = fibonacci(30)
print(f"Fibonacci(30) = {result}")
print(f"Time taken: {time.time() - start_time} seconds")# 清除缓存并再次计算,以比较性能
fibonacci.cache_clear()# 第三次计算斐波那契数列的第30项,这次将没有缓存加速
start_time = time.time()
result = fibonacci(30)
print(f"Fibonacci(30) = {result}")
print(f"Time taken: {time.time() - start_time} seconds")

total_ordering 补全比较方法

通过类中提供的一个或多个比较方法,能够自动为你生成缺失的比较方法。
它基于**提供的最少两个个比较方法(通常是_eq_() 或_lt_())**来推导其他比较方法的逻辑。

from functools import total_ordering@total_ordering
class Circle:def __init__(self, radius):self.radius = radiusdef __eq__(self, other):if isinstance(other, Circle):return self.radius == other.radiusreturn Falsedef __lt__(self, other):if isinstance(other, Circle):return self.radius < other.radiusreturn NotImplemented# 使用示例
c1 = Circle(1)
c2 = Circle(2)
c3 = Circle(3)# 自动生成的比较方法
print(c1 < c2)      # True
print(c2 <= c3)     # True
print(c1 > c3)      # False
print(c1 >= c3)     # False
print(c1 == c1)     # True
print(c1 != c2)     # True

singledispatch 分发泛型函数

主要用途是实现泛型编程,即编写能够处理多种数据类型的函数,而无需为每种数据类型编写单独的函数。
通过使用单分发,你可以定义一个通用的函数名,并根据需要为不同的数据类型提供不同的实现
这有助于保持代码的简洁性和可维护性;


1.单分发机制

rom functools import singledispatch# 使用 singledispatch 装饰器标记一个泛型函数
@singledispatch
def process_data(data):raise NotImplementedError("Unsupported data type")# 为特定类型(例如 str)提供实现
@process_data.register(str)
def _(data):return "Processing string: " + data# 为特定类型(例如 int)提供实现
@process_data.register(int)
def _(data):return "Processing integer: " + str(data)# 为特定类型(例如 list)提供实现
@process_data.register(list)
def _(data):return "Processing list: " + str(data)# 调用泛型函数
print(process_data("Hello"))    # 输出: Processing string: Hello
print(process_data(42))         # 输出: Processing integer: 42
print(process_data([1, 2, 3]))  # 输出: Processing list: [1, 2, 3]# 尝试使用未注册的类型将引发 NotImplementedError
print(process_data(3.14))  # 抛出: NotImplementedError: Unsupported data type

2.自定义多重分发机制
# 自定义分发机制的基础类
class MultiDispatch:def __init__(self, func):self.func = funcself.registry = {}def register(self, *types):def wrapper(f):self.registry[types] = freturn freturn wrapperdef __call__(self, *args, **kwargs):types = tuple(type(arg) for arg in args)if types in self.registry:return self.registry[types](*args, **kwargs)else:return self.func(*args, **kwargs)# 泛型函数
@MultiDispatch
def process_data(data1, data2):raise NotImplementedError("No implementation for these argument types")# 为 (str, int) 组合提供实现
@process_data.register(str, int)
def _(data1, data2):return "Processing string and integer: " + data1 + " " + str(data2)# 为 (int, str) 组合提供实现
@process_data.register(int, str)
def _(data1, data2):return "Processing integer and string: " + str(data1) + " " + data2# 为 (list, list) 组合提供实现
@process_data.register(list, list)
def _(data1, data2):return "Processing lists: " + str(data1) + " and " + str(data2)# 调用泛型函数
print(process_data("Hello", 42))  # 输出: Processing string and integer: Hello 42
print(process_data(42, "World"))  # 输出: Processing integer and string: 42 World
print(process_data([1, 2], [3, 4]))  # 输出: Processing lists: [1, 2] and [3, 4]# 尝试使用未注册的类型组合将引发 NotImplementedError
print(process_data(3.14, "Circle"))  # 抛出: NotImplementedError: No implementation for these argument types

wraps 保留元数据

用于保留原始函数的名称、文档字符串、注解和模块等属性,在创建新的装饰器函数时非常有用;
当你创建一个装饰器时,通常会定义一个函数,该函数接受一个函数作为参数,并返回一个新的函数。
这样做会导致原始函数的某些属性(如 name、doc 等)丢失,因为它们通常会被新函数的属性所覆盖。
@functools.wraps(func)的作用就是在返回新函数时,将这些属性从原始函数复制到新函数,以保持原始函数的“外观”。


import functoolsdef wraps_decorator(func):# 保留原始函数的名称、文档字符串、注解和模块等属性@functools.wraps(func)def wrapper(*args, **kwargs):return func(*args, **kwargs)return wrapper@wraps_decorator
def person(name: str, age: int, city: str) -> None:"""模拟用户个人介绍函数:param name: 姓名:param age:  年龄:param city: 城市:return: None"""print(f"大家好,我叫{name},今年{age}岁,来自{city},我热爱探索和学习,期待与大家共同进步。")# 输出函数名和文档字符串
print("函数名称:", person.__name__)    # 函数名称: person
print("函数文档:", person.__doc__)
# 函数文档:
#     模拟用户个人介绍函数
#     :param name: 姓名
#     :param age:  年龄
#     :param city: 城市
#     :return: None
print("函数词典:", person.__dict__)    # {'__wrapped__': <function person at 0x7fd8f01b4af0>}

update_wrapper 更新元数据

用于手动执行更新包装函数属性的操作,此函数已不被推荐使用
在 Python 3.8 及以后的版本中,@functools.wraps(func) 装饰器已经足够使用,并且是推荐的方式来自动更新包装函数的属性。

import functoolsdef my_decorator(func):# 使用wraps可以保留被装饰函数的元数据# @functools.wraps(func)def wrapper(*args, **kwargs):print("Before function call")result = func(*args, **kwargs)print("After function call")return result# 使用update_wrapper可以手动更新被装饰函数的属性# functools.update_wrapper(wrapper, func)return wrapper@my_decorator
def greet(name):"""Say hello to someone."""return f"Hello, {name}!"# 使用装饰器
print(greet.__name__)  # 输出: greet
print(greet.__doc__)   # 输出: Say hello to someone.
print(greet("World"))  # 输出:# Before function call# Hello, World!# After function call

总结

functools模块是Python中一个功能强大的工具集,提供了多种高阶函数和实用工具,如partial用于函数参数预设,total_ordering简化类的排序逻辑,reduce实现序列累积操作等。这些工具能够简洁高效地处理函数和可迭代对象,提升代码的可读性和可维护性,是Python编程中不可或缺的利器。

这篇关于Python 轻松应对复杂编程 functools模块速查表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/777999

相关文章

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python正则表达式匹配和替换的操作指南

《Python正则表达式匹配和替换的操作指南》正则表达式是处理文本的强大工具,Python通过re模块提供了完整的正则表达式功能,本文将通过代码示例详细介绍Python中的正则匹配和替换操作,需要的朋... 目录基础语法导入re模块基本元字符常用匹配方法1. re.match() - 从字符串开头匹配2.

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

通过Docker容器部署Python环境的全流程

《通过Docker容器部署Python环境的全流程》在现代化开发流程中,Docker因其轻量化、环境隔离和跨平台一致性的特性,已成为部署Python应用的标准工具,本文将详细演示如何通过Docker容... 目录引言一、docker与python的协同优势二、核心步骤详解三、进阶配置技巧四、生产环境最佳实践

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结