惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
量子位
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
Recent Announcements
Recent Announcements
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Security Blog
Microsoft Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
J
Java Code Geeks
V
V2EX
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
博客园 - Franky
爱范儿
爱范儿
T
Tailwind CSS Blog
A
About on SuperTechFans
Google DeepMind News
Google DeepMind News
博客园_首页
B
Blog RSS Feed
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

又见苍岚

COLMAP PatchMatch Stereo 算法详解 事件驱动的状态机框架:从理论到工程实践 Git 在国内网络环境下无法 Push 的排查与修复 —— 配置 Clash 代理 分段五次多项式插值原理详解 路径插值方法深度对比研究 Claude Code 使用指南 OpenClaw 记忆管理与技能创建指南 CBS(Conflict-Based Search)算法详解 A* 算法及其变种详解 OpenClaw 配置多 Agents Windows Powershell 无法加载文件,因为在此系统上禁止运行脚本问题的解决方案 MaxClaw 安装流程 大模型 AI 名词介绍 AList 网盘聚合工具简介 Protobuf 简介与测试 Claude Code 简介以及 GLM 4.7 模型接入 Github 歌词下载工具 163MusicLyrics Python __getattr__ 懒加载 Python TypedDict 机器人仿真平台 Gazebo 安装记录 机器人仿真平台 Gazebo 简介 多机器人路径规划问题(Multi-Agent Path Finding, MAPF)简介 Python exifread 读取修改过的 jpeg 信息错误问题修复 3D 坐标系变换的理解 3D 旋转矩阵基本概念 MongoDB Compass 介绍 Python 环境管理工具 uv Flutter 开发指南 Snipaste 安装下载与黑屏问题解决方案 全局路径规划算法记录
Python Functools
Yiwei Zhang · 2023-08-21 · via 又见苍岚

介绍Python 中内置库 functools —— 可调用对象上的高阶函数和操作 。

简介

Functools 模块用于高阶函数: 作用于或返回其他函数的函数。一般来说,任何可调用对象都可以作为此模块的函数处理。

functools 包含一下模块:

  1. functools.cache
  2. functools.cached_property
  3. functools.cmp_to_key
  4. functools.lru_cache
  5. functools.total_ordering
  6. functools.partial
  7. functools.partialmethod
  8. functools.reduce
  9. functools.singledispatch
  10. functools.singledispatchmethod
  11. functools.update_wrapper
  12. functools.wraps

cache

New in version 3.9.

1
@functools.cache(user_function)

为函数返回结果与输入值创建字典,再次访问时则直接调用结果,缓存没有数量限制。不会删除旧的数据也不会限制内存尺寸,会比 lru_cache(maxsize=size) 轻量且快速。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import functools

@functools.cache
def feb(n):
print('calling n: ', n)
return feb(n-1) + feb(n-2) if n > 2 else 1

print(feb(8))
print(feb(4))
print(feb(10))

-->
calling n: 8
calling n: 7
calling n: 6
calling n: 5
calling n: 4
calling n: 3
calling n: 2
calling n: 1
21
3
calling n: 10
calling n: 9
55

计算过的值就不会重复计算了。

cached_property

New in version 3.8.

1
@functools.cached_property(func)

将类的方法转换为一个属性,该属性的值只计算一次,然后作为实例生命周期的常规属性缓存。

1
2
3
4
5
6
7
8
9
10
import functools

class DataSet:

def __init__(self, sequence_of_numbers):
self._data = tuple(sequence_of_numbers)

@functools.cached_property
def stdev(self):
return statistics.stdev(self._data)

cmp_to_key

New in version 3.2.

1
functools.cmp_to_key(func)

将旧式比较函数转换为关键函数。与接受关键函数的工具一起使用(例如 sort ()、 min ()、 max ()、 heapq.nbest ()、 heapq.nbest ()、 itertools.groupby ())。该函数主要用作从 Python 2转换的程序的转换工具,Python 2支持使用比较函数。

1
sorted(iterable, key=cmp_to_key(locale.strcoll))  # locale-aware sort order

lru_cache

1
2
@functools.lru_cache(user_function)
@functools.lru_cache(maxsize=128, typed=False)

修饰符用制表调用包装函数,该调用最多可以保存最新调用的最大值。当使用相同的参数周期性地调用一个昂贵的或 I/O 绑定的函数时,它可以节省时间。

缓存是线程安全的,因此可以在多个线程中使用包装函数。这意味着在并发更新期间,底层数据结构将保持一致。

不同的参数模式可以被认为是具有不同缓存条目的不同调用。例如,f (a = 1,b = 2)和 f (b = 2,a = 1)的关键字参数顺序不同,可能有两个单独的缓存条目。

如果类型化设置为 true,则将分别缓存不同类型的函数参数。如果类型为 false,则实现通常将它们视为等效调用,并且只缓存一个结果。(有些类型,例如 str 和 int,即使类型为 false,也可以单独缓存。)

1
2
3
4
5
6
7
8
9
@lru_cache(maxsize=32)
def get_pep(num):
'Retrieve text of a Python Enhancement Proposal'
resource = 'https://peps.python.org/pep-%04d/' % num
try:
with urllib.request.urlopen(resource) as s:
return s.read()
except urllib.error.HTTPError:
return 'Not Found'

total_ordering

New in version 3.2.

1
@functools.total_ordering

提供简便的方式定义类比较方法,仅需定义 __lt__(), __le__(), __gt__(), or __ge__() 其中之一和 __eq__() 方法即可完成完备的比较定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@total_ordering
class Student:
def _is_valid_operand(self, other):
return (hasattr(other, "lastname") and
hasattr(other, "firstname"))
def __eq__(self, other):
if not self._is_valid_operand(other):
return NotImplemented
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
if not self._is_valid_operand(other):
return NotImplemented
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))

虽然这个装饰符使得创建行为良好的完全有序的类型变得很容易,但它的代价是执行速度较慢,派生比较方法的堆栈跟踪更复杂。如果性能基准测试表明这是给定应用程序的瓶颈,那么实现所有六种富比较方法可能会提供一个简单的速度提升。

partial

将函数输入以写死的方式减少个数,返回新的函数接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import functools

def show(A, B, C='test'):
print("A: ", A)
print("B: ", B)
print("C: ", C)

new_show = functools.partial(show, 'ABC', C='partial input')
new_show(3)
pass

-->
A: ABC
B: 4
C: partial input

partialmethod

New in version 3.4.

返回一个新的 partial 方法描述符,它的行为类似于 partial,只不过它被设计用作方法定义,而不是直接调用。

相当于该方法修改了原始函数,而不是生成一个输入参数更少的新函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Cell:
def __init__(self):
self._alive = False
@property
def alive(self):
return self._alive
def set_state(self, state):
self._alive = bool(state)
set_alive = partialmethod(set_state, True)
set_dead = partialmethod(set_state, False)

c = Cell()
c.alive

-->
False

c.set_alive()
c.alive

-->
True

reduce

1
functools.reduce(function, iterable[, initializer])

迭代计算函数,从左到右计算,将结果放到最左边,直接进行下一次计算。

reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 等价于 ((((1+2)+3)+4)+5)

1
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])

singledispatch

New in version 3.4.

将函数转换为单分派通用函数。 可以根据输入数据类型不同调用不同的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from functools import singledispatch
@singledispatch
def fun(arg, verbose=False):
if verbose:
print("Let me just say,", end=" ")
print(arg)

@fun.register
def _(arg: int, verbose=False):
if verbose:
print("Strength in numbers, eh?", end=" ")
print(arg)

@fun.register
def _(arg: list, verbose=False):
if verbose:
print("Enumerate this:")
for i, elem in enumerate(arg):
print(i, elem)

# types.UnionType and typing.Union can also be used:
@fun.register
def _(arg: int | float, verbose=False):
if verbose:
print("Strength in numbers, eh?", end=" ")
print(arg)

from typing import Union

@fun.register
def _(arg: Union[list, set], verbose=False):
if verbose:
print("Enumerate this:")
for i, elem in enumerate(arg):
print(i, elem)

@fun.register(float)
@fun.register(Decimal)
def fun_num(arg, verbose=False):
if verbose:
print("Half of your number:", end=" ")
print(arg / 2)

fun_num is fun

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun("Hello, world.")
Hello, world.
fun("test.", verbose=True)
Let me just say, test.
fun(42, verbose=True)
Strength in numbers, eh? 42
fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
Enumerate this:
0 spam
1 spam
2 eggs
3 spam
fun(None)
Nothing.
fun(1.23)
0.615

singledispatchmethod

New in version 3.8.

要为类定义一个泛型方法,可以使用 @singlepatchmethod 装饰符对其进行装饰。当使用 @singlepatchmethod 定义函数时,请注意调度发生在第一个非 self 或 non-cls 参数的类型上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Negator:
@singledispatchmethod
def neg(self, arg):
raise NotImplementedError("Cannot negate a")

@neg.register
def _(self, arg: int):
return -arg

@neg.register
def _(self, arg: bool):
return not arg

@singlepatchmethod 支持与其他修饰符(如@classmethod)嵌套。注意,为了支持 patcher.register,singlepatchmethod 必须是最外层的装饰器。下面是 Negator 类,其中的 neg 方法绑定到该类,而不是该类的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Negator:
@singledispatchmethod
@classmethod
def neg(cls, arg):
raise NotImplementedError("Cannot negate a")

@neg.register
@classmethod
def _(cls, arg: int):
return -arg

@neg.register
@classmethod
def _(cls, arg: bool):
return not arg

update_wrapper

New in version 3.2

1
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

更新一个包装函式,使其看起来像包装好的函数。可选参数是元组,用于指定原始函数的哪些属性被直接分配给包装函式上的匹配属性,以及哪些包装函式属性被更新为原始函数的相应属性。

wraps

1
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

这是一个简写函数,用于在定义包装函式时调用 update_wrapper() 作为函数修饰符。它等效于partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from functools import wraps
def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
print('Calling decorated function')
return f(*args, **kwds)
return wrapper

@my_decorator
def example():
"""Docstring"""
print('Called example function')

-->
example()
Calling decorated function
Called example function
example.__name__
'example'
example.__doc__
'Docstring'

参考资料

文章链接:
https://www.zywvvd.com/notes/coding/python/python-functools/python-functools/