「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
函数装饰器
函数装饰器用于在源码中“标记”函数,以某种方式增强函数的行为。
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。 装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
举个栗子:
1 | python复制代码@decorate |
装饰器的一大特性是,能把被装饰的函数替换成其他函数。第二个特性是,它们在被装饰的函数定义之后立即运行;这通常是在导入时(即 Python 加载模块时)。
装饰器通常在一个模块中定义,然后应用到其他模块中的函数上。
大多数装饰器会在内部定义一个函数,然后将其返回。
标准库中的装饰器
Python 内置了三个用于装饰方法的函数:property
、classmethod
和 staticmethod
。
另一个常见的装饰器是 functools.wraps
,它的作用是协助构建行为良好的装饰器。标准库中最值得关注的两个装饰器是 lru_cache
和 singledispatch
(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
,把不同参数类型得到的结果分开保存,即把通常认为相等的浮点数和整数参数(如 1
和 1.0
)区分开。
因为 lru_cache
使用字典存储结果,而且键根据调用时传入的定位参数和关键字参数创建,所以被 lru_cache
装饰的函数,它的所有参数都必须是可散列的。
functools.singledispatch
因为 Python 不支持重载方法或函数,所以我们不能使用不同的签名定义同一函数名的函数的变体,也无法使用不同的方式处理不同的数据类型。在 Python 中,一种常见的做法是使用一串 if/elif/elif
,调用专门的函数,如 functionA_str
、functionA_int
,等等。这样不便于模块的用户扩展,还显得笨拙:时间一长,分派函数 functionA
会变得很大,而且它与各个专门函数之间的耦合也很紧密。
functools.singledispatch
装饰器可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。使用 @singledispatch
装饰的普通函数会变成泛函数(generic function): 根据第一个参数的类型,以不同方式执行相同操作的一组函数。
可以在系统的任何地方和任何模块中注册专门函数。
举个栗子:
1 | python复制代码from functools import singledispatch |
参数化装饰器
让装饰器接受其他参数:创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。
举个栗子:
1 | python复制代码registry = set() |
本文转载自: 掘金