预备知识
首先我们要知道在python,一切皆对象,函数也是一个对象
>>> def test():
... return "Hello World"
有自己的id值,有type,有自己的值
>>> id(test)
140155005410568
>>> type(test)
<class 'function'>
>>> test
<function test at 0x7f78614f9d08>
甚至可以赋值给其他变量
>>> test1 = test
>>> test1()
'Hello World'
哪怕是当做参数传递给别的函数,也可以当做函数的返回值
>>> def foo(func):
... print(func)
... return func
...
>>> test2 = foo(test)
<function test at 0x7f78614f9d08>
>>> test2()
'Hello World'
装饰器定义
装饰器本质其实就是一个函数, 可以让其它函数不改动源代码的情况下增加其他新功能, 比如网站经常需要的权限校验等场景
最初的函数
def add(x, y):
print(x+y)
add(1,2)
现在我们有一个新需求, 计算代码执行时间
import time
def add(x, y):
start_time = time.time
print(x+y)
stop_time = time.time
print("{func} spend {time} ".format(func = "add", time = stop_time-start_time))
add(1,2)
我们当然可以这么写, 但是一来修改了源代码可能会造成一些未知的错误, 二来如果我们有一百个函数, 这样写也不现实, 这就是我们装饰器出场的时候了.
创建一个装饰器
import time
def timmer(func):
"""
:param func: 被装饰的函数
:return: 一个计算函数运行时间的函数
"""
def wrapper(*args, **kwargs):
"""
:param args:收集被装饰函数的参数
:param kwargs:收集被装饰函数的关键字参数
:return:
"""
start_time = time.time()
# 让进程睡一秒
time.sleep(1)
# 调用被装饰的函数
result = func(*args, **kwargs)
stop_time = time.time()
print("{func} spend {time} ".format(func = "add", time = stop_time-start_time))
return result
return wrapper
使用装饰器
def add(x, y):
print(x,y)
# 因为timmer返回的是wrapper函数对象,所以执行add()相当于执行wrapper()
add = timmer(add)
add(1,2)
如果觉得还是麻烦那就通过一个语法糖@符号来使用装饰器
@timmer
def add(x, y):
print(x,y)
add(1,2)
这就是最基本的装饰器, 在不修改源代码的前提下为函数添加一个新功能, 调用时只需要在原函数上方添加一个 @deco_name , 在这里是@timmer
带参数的装饰器
python还允许我们给装饰器带上函数
import time
def timmer(flag):
"""
:param flag: 接收装饰器的参数
:return:
"""
def outer_wrapper(func):
"""
:param func: 接收被装饰的函数
:return:
"""
# 接收被装饰函数的参数
def wrapper(*args, **kwargs):
"""
:param args: 收集被装饰函数的参数
:param kwargs: 收集被装饰函数的关键字参数
:return:
"""
if flag == "true":
start_time = time.time()
# 调用被装饰的函数
result = func(*args, **kwargs)
# 让进程睡一秒
time.sleep(1)
stop_time = time.time()
print("{func} spend {time} ".format(func="add", time=stop_time - start_time))
return result
else:
print("Unexpected ending")
return wrapper
return outer_wrapper
通过一个语法糖@符号来使用装饰器
所谓的语法糖便是你不使用也可以完成任务,但是使用它可以让你的代码更简洁
@timmer(flag="false")
def add(x, y):
print(x, y)
add(1,2)
被多个装饰器装饰
当函数被多个装饰器装饰时,从里向外装饰
@a
@b
@c
def func():
pass
相当于
func = a(b(c(func)))