Python Functools 模块

2024 年 8 月 29 日 | 阅读 19 分钟

functools 模块是 Python 标准库中包含的,它为使用高阶函数(返回函数或将另一个函数作为参数的函数)提供了基本功能。您可以使用这些功能重用或增强函数或可调用对象的实用性,而无需重写它们。这简化了构建可重用和可维护代码的过程。

functools 模块在当前稳定版本 Python 3.8 系列中有 11 个函数,尽管其中一些函数在以前或以后的版本中可能无法访问或工作方式不同。它们如下:

  • reduce()
  • lru_cache()
  • partial()
  • partialmethod()
  • singledispatch()
  • singledispatchmethod()
  • cached_property()
  • total_ordering()
  • update_wrapper()
  • wraps()
  • cmp_to_key()

Reduce()

我们将从一个经典函数开始。reduce(function, sequence) 函数接受一个函数和一个可迭代对象。它以累积方式从左到右将提供的函数应用于可迭代对象的所有成员,然后返回一个单一值。

简单地说,它首先将参数函数应用于可迭代对象的前两项,此初始调用返回的值成为函数的第一个参数,而可迭代对象的第三个元素成为第二个参数。这种方法一直持续到可迭代对象用尽。

lru_cache()

lru_cache() 是一个装饰器,它将一个函数包装在一个记忆化的可调用对象中,该对象最多保存一个函数调用的结果,并在再次使用相同输入调用该函数时返回存储的值。当一个昂贵的或 I/O 绑定的函数重复使用相同参数调用时,它可以节省时间。

它主要采用两种数据结构:一个字典用于将函数的参数映射到其输出,一个链表用于跟踪函数的调用历史。

LRU Cache 的全称是 Least-Recently-Used Cache(最近最少使用缓存),它指的是当达到最大条目数时,会淘汰最近最少使用的元素。如果将 maxsize 设置为 None,则禁用 LRU 功能;如果 typed 为 True,则 LRU 功能会分别缓存不同数据类型的参数,例如,f(3) 和 f(3.0) 将分别缓存。

通常,LRU 缓存只应在需要重用先前计算的值时使用。因此,缓存每次调用都会产生不同可变对象的函数并不是一个好主意。函数的 positional 和 keyword 输入也必须是可哈希的,因为需要一个字典来缓存结果。

partial()

偏函数是已经分配了一些输入参数的派生函数。如果一个函数接受两个参数,比如“a”和“b”,则可以从中构建一个以“a”作为预填充参数的偏函数,然后可以仅以“b”作为参数调用它。Functool 中的 partial() 方法用于构造偏函数/对象,这是一个重要的特性,因为它允许:

  • 复制具有某些已设置参数的现有函数。
  • 以良好文档的方式创建现有函数的新版本。

偏函数还具有可用于跟踪偏函数和对象的有价值的特性。其中一些是:

  • args - 返回偏函数的预分配位置参数。
  • keywords - 返回偏函数的预分配关键字参数。
  • func - 返回父函数的名称及其位置。

partialmethod()

partialmethod() 方法提供了一个新的偏方法描述符,它类似于 partial,但旨在用作方法规范而不是可调用方法。你可以将其视为方法的 partial()。

singledispatch()

在详细介绍此函数之前,首先回顾两个关键概念至关重要:

第一个是通用函数,它是由许多函数组成的,这些函数都为不同的类型执行相同的任务。分派算法确定在调用期间将使用哪个实现。

第二个是单分派,它是一种通用函数分派,其中实现由单个参数的类型决定。

考虑到这一点,functool 中的 singledispatch 装饰器将一个基本函数转换为一个通用函数,其行为由其第一个参数的类型决定。用通俗易懂的话来说,它用于函数重载。

singledispatchmethod()

它是一个装饰器,其工作方式与 @singledispatch 相同,但用于方法而不是函数。

cached_property()

cached_property() 装饰器将类方法转换为属性,其值仅计算一次,然后在其实例的整个生命周期中作为普通属性进行缓存,正如其名称所示。除了缓存功能外,它与 @property 类似。它对于通常在功能上是永久但计算成本高的实例属性非常有用。

total_ordering()

给定一个定义一个或多个富比较排序方法(等价于、=、>、>= 和 ==)的类,例如 __lt__()、__le__()、__gt__()、__ge__() 或 __eq__()。您可以定义一些比较方法,@total_ordering 将根据您提供的定义提供其余方法。类中包含 __eq__() 方法至关重要。

例如,如果您想创建一个比较不同数字的类。几乎肯定需要实现所有丰富的比较方法。然而,由于这可能既乏味又不必要,您可以只实现 __eq__ 和 __gt__ 方法,并依靠 @total_ordering 来填补空白。

update_wrapper()

它使包装器函数的元数据看起来像被包装的函数。对于偏函数,update_wrapper(partial, parent) 将更新偏函数的文档(doc)和名称(name)以匹配父函数。

wraps()

它只是在被装饰函数上调用 update_wrapper() 的一个快捷方式。它与调用 partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated, wrapped=wrapped) 相同。

cmp_to_key();

它将旧式比较函数转换为键函数。任何接受两个参数、比较它们并返回小于为负数、相等为零或大于为正数的可调用对象都被称为比较函数。operator.itemgetter() 键函数是接受一个参数并返回另一个值用作排序键的可调用对象的一个示例。sorted()、min()、max() 和 itertools.groupby 等工具使用键函数()。

cmp_to_key() 主要用作 Python 2 脚本与比较函数的过渡辅助。

让我们看看代码

代码

输出

Enter your choice according to the below-listed options::
1. To use the lru_cache() function and compare the time difference between the function with and without the lru_cache().
2. To use the singledispatch() function and compare the time usage of the functions with and without the singledispatch().
3. To use the reduce() function understand the various use-cases of the reduce().
4. To use the partial() function and compare the usage of the functions with and without the partial().
5. To exit from the code execution.
1
For how many iterations do you want to calculate::
33
Function with lru_cache took -2.7494192123413086 seconds to calculate Fibonacci for 33 iterations.
Function without lru_cache took -1.7327818870544434 seconds to calculate Fibonacci for 33 iterations.
To move ahead with code execution enter [y] else [n]
y
Enter your choice according to the below-listed options::
1. To use the lru_cache() function and compare the time difference between the function with and without the lru_cache().
2. To use the singledispatch() function and compare the time usage of the functions with and without the singledispatch().
3. To use the reduce() function understand the various use-cases of the reduce().
4. To use the partial() function and compare the usage of the functions with and without the partial().
5. To exit from the code execution.
2
calling the divide function with the integer parameters( divide_the_numbers(6/4) ), the result is ::
1.5
calling the divide function with the string parameters( divide_the_numbers('12'/'7') ), the result is ::
12/7
To move ahead with code execution enter [y] else [n]
y
Enter your choice according to the below-listed options::
1. To use the lru_cache() function and compare the time difference between the function with and without the lru_cache().
2. To use the singledispatch() function and compare the time usage of the functions with and without the singledispatch().
3. To use the reduce() function understand the various use-cases of the reduce().
4. To use the partial() function and compare the usage of the functions with and without the partial().
5. To exit from the code execution.
3
Enter the length of the list::
5
100
234
665
230
874
entered list is [100, 234, 665, 230, 874]
Using reduce to find the sum of all the elements of the list::
The result of the sum is 2103
Using reduce to find the maximum among all the elements of the list::
Max element is 874
To move ahead with code execution enter [y] else [n]
y
Enter your choice according to the below-listed options::
1. To use the lru_cache() function and compare the time difference between the function with and without the lru_cache().
2. To use the singledispatch() function and compare the time usage of the functions with and without the singledispatch().
3. To use the reduce() function understand the various use-cases of the reduce().
4. To use the partial() function and compare the usage of the functions with and without the partial().
5. To exit from the code execution.
4
Calling the function twice without the use of partial
Enter the occupation::
Driver
Enter the gadgets used::
Cars
Cars are used by the Driver
Enter the occupation::
Electrician
Enter the gadgets used::
Screwdriver
Screwdrivers are used by the Electrician
Calling the function twice with the use of partial
Enter the occupation::
Teacher
Enter the gadgets used::
Books
Books are used by the Teacher
Enter the gadgets used::
Pens
Pens are used by the Teacher
To move ahead with code execution enter [y] else [n]
y
Enter your choice according to the below-listed options::
1. To use the lru_cache() function and compare the time difference between the function with and without the lru_cache().
2. To use the singledispatch() function and compare the time usage of the functions with and without the singledispatch().
3. To use the reduce() function understand the various use-cases of the reduce().
4. To use the partial() function and compare the usage of the functions with and without the partial().
5. To exit from the code execution.
3
Enter the length of the list::
4
87732
34853
3421
45214
entered list is [87732, 34853, 3421, 45214]
Using reduce to find the sum of all the elements of the list::
The result of the sum is 171220
Using reduce to find the maximum among all the elements of the list::
Max element is 87732
To move ahead with code execution enter [y] else [n]
y
Enter your choice according to the below-listed options::
1. To use the lru_cache() function and compare the time difference between the function with and without the lru_cache().
2. To use the singledispatch() function and compare the time usage of the functions with and without the singledispatch().
3. To use the reduce() function understand the various use-cases of the reduce().
4. To use the partial() function and compare the usage of the functions with and without the partial().
5. To exit from the code execution.
5

一个示例程序,用于理解此 Python 模块提供的各种函数的用例场景,在此程序中,我们编写了一个类,其中包含单独的函数,其中一些函数或主调用函数表示此 Python 模块提供的不同函数的各种用例场景。这些不同的函数用于表示这些函数的特定用例场景,但这些函数的用途可以根据用户的需要或要求而变化。实际调用以展示场景的函数实际上调用了此模块的内置函数,这些函数实际实现了这些函数的功能。

让我们来看看 Python 中 Functools 模块的一些优点

缓存

让我们从 functools 模块最基本但最强大的函数开始。lru_cache、cache 和 cached_property 是缓存函数(也是装饰器)。第一个是 lru_cache,它提供了函数结果的最近最少使用缓存,换句话说,结果备忘录。如果您想要更精细的缓存,可以使用 typed=true 参数,它会单独缓存不同类型的参数。

一个名为 cache 的函数是 functools 中的另一个缓存装饰器。它是 lru_cache 的一个简单包装器,省略了 max_size 选项,使其更小,并且在它不需要逐出旧数据之后,它不需要逐出旧值。您还可以使用 cached_property,这是另一个用于缓存的装饰器。顾名思义,它用于缓存类属性结果。如果您的属性计算成本高昂且不可变,这尤其有利。cached_property 的优点是它只在查找时执行,允许我们更改属性。更新属性后,以前缓存的值将不会被使用;相反,将计算并缓存一个新值。清除缓存也是可能的;我们所要做的就是删除属性。

比较和排序

您可能已经知道,您可以使用 __lt__、__gt__ 或 __eq__ 在 Python 中实现比较运算符,例如 >= 或 ==。实现所有 __eq__、__lt__、__le__、__gt__ 或 __ge__ 函数可能会很痛苦。幸运的是,functools 模块提供了 @total_ordering 装饰器,它可以帮助我们解决这个问题——我们所要做的就是实现 __eq__ 和其余方法中的一个,装饰器将处理其余的工作。

重载

我们可能都曾被告知 Python 中不可能进行函数重载,但实际上使用 functools 模块中的两个函数:singledispatch 和/或 singledispatchmethod,很容易实现。这些方法有助于实现多重分派算法,该算法允许像 Python 这样的动态类型编程语言在运行时区分类型。

支持异步操作

我们都使用各种外部库和框架,其中许多都有需要我们传入回调函数的函数和接口,例如异步操作或事件监听器。这并不新鲜,但是如果我们除了回调函数之外还需要传入其他参数怎么办?这时 functools 就派上用场了。partial 很有用,因为它可以用来冻结函数的某些(或所有)参数,从而生成一个具有更简单函数签名的新对象。通常在读取文件时,我们希望遍历行,但在二进制数据的情况下,我们可能更喜欢遍历固定大小的记录。这可以通过编写一个 partial 可调用对象来实现,该对象读取定义的数据块并将其传递给 iter,iter 从中生成一个迭代器。然后,此迭代器执行读取方法,直到到达文件末尾,始终只接受 RECORD_SIZE 参数指定的数据。当文件末尾到达时,返回 sentinel 值 (b"),循环结束。

装饰器

我们已经在前面的部分讨论了几个装饰器,但没有讨论用于构造更多装饰器的装饰器。functools.wraps 就是这样一个装饰器。

所以,在这篇文章中,我们理解了 Python 中的 Functools 模块。