#########装饰器的理解与应用########
# 装饰器的概念
- 装饰器的实现是函数里面嵌套函数;
- 装饰器的本质是一个函数, 它可以让其他函数在不需要做任何代码改动的前提下增加额外的功能;
- 装饰器需要传递一个函数, 返回值也是一个函数对象.
# 装饰器的应用场景
# 计时器
def timeit(fun):
def wrapper(*args, **kwargs): # 接收可变参数和关键字参数
# args: 元组 kwargs: 字典 args=(5,)
# 函数执行之前
start_time = time.time()
# 执行函数
fun(*args, **kwargs) # args解包, 对于元组解包, 对于字典解包
# 执行函数之后
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return wrapper
import time
import functools
def timeit(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
start_time = time.time()
time.sleep(3)
res = fun(*args, **kwargs)
end_time = time.time()
print('运行时间为%.6f' % (end_time - start_time))
return res
return wrapper
@timeit #time1 = timeit(time)
def time1(n):
return [i * 2 for i in range(n)]
print(time1(20))
functools模块可以保留被装饰函数的函数名和帮助文档信息.
# 记录日志
# 用户登录验证
# 函数参数验证
# 案例:
# 用户使用ATM的时候, 执行程序之前祝福语==“您好”,
# 执行程序之后打个广告=="欢迎使用".
import functools
def atm(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
print('您好')
fun(*args, **kwargs)
print('欢迎使用')
return wrapper #返回值是嵌套的函数对象
@atm
def login():
print( 'login...')
login()
# 装饰器之函数属性变化的解决方案
# 问题1: 装饰的函数有返回值的解决方法:
# 问题2:如何保留被装饰函数的函数名和帮助文档信息. @functools.wraps(fun)
我们拿记时器装饰器举例:
def timeit(fun): # fun_list
"""这是一个装饰器timeit"""
@functools.wraps(fun)
# 可以保留被装饰函数的函数名和帮助文档信息.
def wrapper(*args, **kwargs): # 接收可变参数和关键字参数
"""这是一个wrpper函数"""
# args: 元组 kwargs: 字典 args=(100,)
# 函数执行之前
start_time = time.time()
# 执行函数
res = fun(*args, **kwargs) # args解包, 对于元组解包, 对于字典解包
# fun_list(100)
# 执行函数之后
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return res
return wrapper
import functools
import time
def timeit(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
start_time = time.time()
time.sleep(3)
res = fun(*args, **kwargs)
end_time = time.time()
print(end_time-start_time)
return res
return wrapper
@timeit
def qwer(n):
"""
这是qwer函数, 被timeit装饰
"""
return [i ** 2 for i in range(n)]
print(qwer(20))
print(qwer.__name__)
print(qwer.__doc__)
运行结果:
在不引入functools模块时,函数名称以及注释将不是源函数qwer的名称及注释,因为源函数经过装饰器里的wrapper函数执行后,
记录的函数名称以及注释是wrapper函数的,运行结果如下:
# 问题:如何给函数添加日志
运行代码与结果如下:
import functools
import time
def log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print(time.ctime(), fun.__name__, (end_time - start_time), res)
return res
return wrapper
@log
def sum(x, y):
time.sleep(1)
return x + y
print(sum(2, 1))
# 装饰器的第二种方式
# 需求: 用户登陆验证的装饰器is_login
# 1). 如果用户登陆成功, 则执行被装饰的函数;
# 2). 如果用户登陆不成功, 则执行登陆函数
import functools
login_users = ['admin', 'root']
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
if kwargs.get('name') in login_users:
res = fun(*args, **kwargs)
return res
else:
res=login()
return res
return wrapper
@is_login
def writeblog(name):
return '编写博客'
def login():
return '登陆...'
print(writeblog(name='admin1'))
## 带有多个装饰器的函数
# 需求1: 用户登陆验证的装饰器is_login
# 1). 如果用户登陆成功, 则执行被装饰的函数;
# 2). 如果用户登陆不成功, 则执行登陆函数
#
# 需求2: 判断登陆用户是否未管理员is_admin(此处管理员只有一个为:admin用户)
# 1).如果用户为管理员, 则执行被装饰的函数;
# 2).如果用户不是管理员, 则报错;
运行代码如下:
import functools
login_users = ['admin', 'root']
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
if kwargs.get('name') == 'admin':
res = fun(*args, **kwargs)
return res
else:
return 'error'
return wrapper
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
if kwargs.get("name") in login_users:
res = fun(*args, **kwargs)
return res
else:
res = login()
return res
return wrapper
@is_admin
@is_login
def writeBlog(name):
return "编写博客"
def login():
return "登陆..."
print(writeBlog(name='admin'))
经过测试由此可见:
# 当有多个装饰器时, 从下到上调用装饰器,
# 真实wrapper内容执行是从上到下执行.
def makebold(fun):
print("bold1")
def wrapper1(*args, **kwargs):
print("bold2")
return fun(*args, **kwargs) # wrapper
return wrapper1
def makei(fun): # fun=login
print("i1")
def wrapper(*args, **kwargs):
print("i2")
return fun(*args, **kwargs)
return wrapper
@makebold # login = makebold(login) # login为wrapper1
@makei # login = makei(login) # login为wrapper
def login():
return "登陆"
print(login())
##带有参数的装饰器
# 创建装饰器, 要求如下:
# 1. 创建add_log装饰器, 被装饰的函数打印日志信息;
# 2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx
import functools
import time
# format
def log(kind): # kind="debug"
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
# run_time = time.ctime()
# fun_name = fun.__name__
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print("<%s> [%s] 函数名: %s, 运行时间:%.5f, 运行返回值结果:%d"
%(kind, time.ctime(), fun.__name__, end_time-start_time, res )
)
return res
return wrapper
return add_log
@log("debug")
# log("debug")==> 返回值是add_log
# add=add_log(add)
def add(x,y):
time.sleep(0.1)
return x+y
print(add(1,2))
# wrapper(1,2)