Python装饰器
装饰器在Python中是一个强大的高级用法,并且在流行Python框架中变得越来越常见。经常会用到装饰器来增强函数的行为(动态的给一个对象添加一些额外的职责),包括记录日志,权限校验,性能测试,数据封装等。有了装饰器,我们可以抽离出大量和函数功能本身无关的雷同代码并继续重用。
Python装饰器有两种:
- 函数装饰器:管理函数调用和函数对象
- 类装饰器:管理类实例和类自身
为什么使用装饰器?
经常会遇到给函数或类增加新功能的场景,当然我们可以使用函数调用或者其它技术来实现,但是使用装饰器意图明确,最小化扩展代码的冗余,使用@语法糖,相对优雅。
装饰器的原理是什么?
我们先来看一个最简单的装饰器:
import time
from functools import wraps
def time_it(func):
"""
输出函数的运行时间
:param func:
:return:
"""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func()
end_time = time.time()
process_time = end_time - start_time
print(func.__name__, process_time)
return result
return wrapper
@time_it
def func_a():
time.sleep(2)
对上述代码进行解释:
- time_it返回wrapper函数对象
- 使用time_it装饰func_a函数
- 调用被装饰的func_a函数会运行wrapper函数,func_a其实是wrapper的引用
原理:我们知道Python中一切皆对象,可以将函数作为其它函数的返回值。可以看到,装饰器的本质是一个函数,返回一个函数对象,通过"@"语法糖在包装函数中引入装饰器。
装饰器的一个关键特性是,在被装饰的函数定义之后立即执行。
@wraps
上述装饰器中用到的了@wraps(func),在创建装饰器时,一定要记得为包装函数添加functools库中的@wraps装饰器,以保证函数的元数据(包括函数名,函数注解等)不被丢失。
当我们需要访问为被装饰器修饰的原包装函数时,可以使用@wraps的__wrapped__
属性来访问。
内置装饰器
Python有三个内置装饰器:@staticmathod、@classmethod和@property
- @staticmethod:类的静态方法,跟成员方法的区别是没有self参数,并且可以在类不进行实例化的情况下调用。
- @classmethod:跟成员方法的区别是接收的第一个参数不是self,而是cls(当前类的具体类型)
- @property:表示可以直接通过类实例直接访问的信息。
装饰器嵌套
为了支持多步骤的扩展,装饰器语法允许我们向一个装饰的函数或方法添加多个装饰器,若多个装饰器同时装饰一个函数,那么装饰器的调用顺序和@语法糖的声明顺序相反,也就是:
@decorator1
@decorator2
def func():
pass
等效于:
func = decorator1(decorator2(func()))
装饰器参数
函数装饰器和类装饰器都能接收参数,这些参数传递给了真正返回装饰器的可调用对象,而装饰器反过来又返回一个可调用对象。
装饰器参数在装饰发生之前就解析了,并且它们通常用来保持状态信息供随后的调用使用。
上述实例中,func_a()是没有参数的,那如果添加参数的话,装饰器该如何编写以接收参数呢?可以在装饰器中使用*args和**kwargs代替参数:
def time_it(func):
"""
输出函数的运行时间
:param func:
:return:
"""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
process_time = end_time - start_time
print(func.__name__, process_time)
return result
return wrapper
带参数的装饰器
我们有时候需要提供给被装饰的函数特定的功能,需要在装饰器中带参数。比如在业务处理中我们需要限定函数的执行超时时间,由于每个函数所对应的超时时间不一样,所以需要在装饰器中带参数以实现。
装饰器的语法允许我们在调用时,提供其它参数,实现上述场景:
import time
import signal
import functools
def func_timeout(timeout):
"""
超时时间装饰器
:param timeout:
:return:
"""
def decorator(func):
def handler(signum, frame):
raise RuntimeError("run %s timeout !" % func.__name__)
@functools.wraps(func)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, handler)
signal.alarm(timeout)
func(*args, **kwargs)
signal.alarm(0)
return wrapper
return decorator
@func_timeout(timeout=10)
def func():
time.sleep(11)
print("#" * 100)
if __name__ == '__main__':
func()
类装饰器
上述实例都是函数装饰器,相比函数装饰器,类装饰器更加灵活,主要依靠类的__call__
方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。
举个例子:
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
print("class decorator start")
self._func(*args, **kwargs)
print("class decorator end")
@Foo
def func():
print("test123")
if __name__ == '__main__':
func()
以上,代码见 my github。
- objective-C中如何判断一个类中有没有定义某个方法
- Steve Boswell:智能口罩让PM2.5滚蛋
- kvm虚拟化管理平台WebVirtMgr部署-完整记录(2)
- objective-C中的扩展方法与partial class
- 仿优酷Android客户端图片左右滑动(自动滑动)
- objective-C: NSString应该用initWithFormat? 还是 stringWithFormat?
- objective-C 的内存管理之-实例分析
- Tim Berners-Lee:网络的自由和开放
- android防止内存溢出浅析
- objective-C 的内存管理之-自动释放池(autorelease pool)
- objective-C 的内存管理之-引用计数
- CompoundButton.OnCheckedChangeListener与RadioGroup.OnCheckedChangeListener冲突
- Liora Rosin & Golan Levi:在北京驾车看洛杉矶的落日
- iphone/ipad/itouch进入DFU模式最简单的操作办法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法