【python】函数装饰器 与 常用装饰器 函数装饰器 标准

「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

函数装饰器

函数装饰器用于在源码中“标记”函数,以某种方式增强函数的行为。

装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。 装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。

举个栗子:

1
2
3
4
5
6
7
8
9
python复制代码@decorate
def target():
print('running target()')

# 相当于
def target():
print('running target()')

target = decorate(target)

装饰器的一大特性是,能把被装饰的函数替换成其他函数。第二个特性是,它们在被装饰的函数定义之后立即运行;这通常是在导入时(即 Python 加载模块时)。

装饰器通常在一个模块中定义,然后应用到其他模块中的函数上。

大多数装饰器会在内部定义一个函数,然后将其返回。

标准库中的装饰器

Python 内置了三个用于装饰方法的函数:propertyclassmethodstaticmethod

另一个常见的装饰器是 functools.wraps ,它的作用是协助构建行为良好的装饰器。标准库中最值得关注的两个装饰器是 lru_cachesingledispatch (Python 3.4 新增)。 这两个装饰器都在 functools 模块中定义。

functools.lru_cache

functools.lru_cache 实现了备忘 (memoization)功能。它把耗时的函数的结果保存起来,避免传入相同的参数时重复计算。LRU 三个字母是 “Least Recently Used” 的缩写,表明缓存不会无限制增长,一段时间不用的缓存条目会被扔掉。

lru_cache 可以使用两个可选的参数来配置。它的签名是:

1
python复制代码functools.lru_cache(maxsize=128, typed=False)

maxsize 参数指定存储多少个调用的结果。缓存满了之后,旧的结果会被扔掉,腾出空间。为了得到最佳性能,maxsize 应该设为 2 的幂。 typed 参数如果设为 True ,把不同参数类型得到的结果分开保存,即把通常认为相等的浮点数和整数参数(如 11.0 )区分开。

因为 lru_cache 使用字典存储结果,而且键根据调用时传入的定位参数和关键字参数创建,所以被 lru_cache 装饰的函数,它的所有参数都必须是可散列的。

functools.singledispatch

因为 Python 不支持重载方法或函数,所以我们不能使用不同的签名定义同一函数名的函数的变体,也无法使用不同的方式处理不同的数据类型。在 Python 中,一种常见的做法是使用一串 if/elif/elif ,调用专门的函数,如 functionA_strfunctionA_int ,等等。这样不便于模块的用户扩展,还显得笨拙:时间一长,分派函数 functionA 会变得很大,而且它与各个专门函数之间的耦合也很紧密。

functools.singledispatch 装饰器可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。使用 @singledispatch 装饰的普通函数会变成泛函数(generic function): 根据第一个参数的类型,以不同方式执行相同操作的一组函数。

可以在系统的任何地方和任何模块中注册专门函数。

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
python复制代码from functools import singledispatch
import numbers
import html

@singledispatch # 标记处理 object 类型的基函数
def htmlize(obj):
content = html.escape(repr(obj))
return '<pre>{}</pre>'.format(content)

@htmlize.register(str) # 各个专门函数使用 @«base_function».register(«type») 装饰。
def _(text): # 专门函数的名称无关紧要;_ 是个不错的选择
content = html.escape(text).replace('\n', '<br>\n')
return '<p>{0}</p>'.format(content)

@htmlize.register(numbers.Integral)
def _(n):
return '<pre>{0} (0x{0:x})</pre>'.format(n)

参数化装饰器

让装饰器接受其他参数:创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
python复制代码registry = set()

def register(active=True): # 接受一个可选的关键字参数
def decorate(func): # 真正的装饰器
print('running register(active=%s)->decorate(%s)' % (active, func))

if active: # 只有 active 参数的值(从闭包中获取)是 True 时才注册 func
registry.add(func)
else: # 如果 active 不为真,而且 func 在 registry 中,那么把它删除
registry.discard(func)
return func # decorate 是装饰器,必须返回一个函数

return decorate

@register(active=False) # @register 工厂函数必须作为函数调用,并且传入所需的参数
def f1():
print('running f1()')

@register() # 即使不传入参数,register 也必须作为函数调用,即要返回真正的装饰器 decorate
def f2():
print('running f2()')

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%