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

相关文章

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

python 字典d[k]中key不存在的解决方案

《python字典d[k]中key不存在的解决方案》本文主要介绍了在Python中处理字典键不存在时获取默认值的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录defaultdict:处理找不到的键的一个选择特殊方法__missing__有时候为了方便起见,

使用Python绘制可爱的招财猫

《使用Python绘制可爱的招财猫》招财猫,也被称为“幸运猫”,是一种象征财富和好运的吉祥物,经常出现在亚洲文化的商店、餐厅和家庭中,今天,我将带你用Python和matplotlib库从零开始绘制一... 目录1. 为什么选择用 python 绘制?2. 绘图的基本概念3. 实现代码解析3.1 设置绘图画

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur