15 Python学习之装饰器
装饰器
装饰器:在不改变源函数的代码及调用方式的前提下,为其增加新的功能,装饰器就是一个函数,他的本质是闭包
装饰器开放封闭的原则:
开放:对代码的扩展开放
封闭:对源码的修改是封闭的
被装饰函数无参数,无返回值
例1:
import time
def timer(func):
def inner():
start_time = time.time()
func()
end_time = time.time()
print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
return inner # 闭包,返回内层函数的引用
# show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info = timer(show_info)的简写,官方叫做语法糖
@timer
def show_info():
print(f"我的姓名是:张三")
time.sleep(2)
show_info()
运行结果:
我的姓名是:张三
装饰器打印执行时间为:2.00051
被装饰函数有返回值
被装饰函数有返回值,那么在设计装饰器的时候,就应该有变量进行接收,然后再将该返回值返回给调用者
例1:
import time
def timer(func):
def inner():
start_time = time.time()
inner_ret = func() # 调用原函数并接受返回值
end_time = time.time()
print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
return inner_ret # 将调用原函数的返回值返回给调用者
return inner # 闭包,返回内层函数的引用
@timer # show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info = timer(show_info)的简写,官方叫做语法糖
def show_info():
print(f"我的姓名是:张三")
time.sleep(2)
return 'show_info函数的返回值'
ret = show_info() # ret接受的是inner的返回值inner_ret
print(ret)
我的姓名是:张三
装饰器打印执行时间为:2.00168
show_info函数的返回值
被装饰函数有参数
被装饰函数有参数,由于实际调用的时候,掉的是闭包中的内部函数,所以内部函数相应的也要设计成跟原函数一样的带参数
例1:
import time
def timer(func):
def inner(name): # 设计时需要添加参数
start_time = time.time()
inner_ret = func(name) # 调用原函数indxe,传入参数并接受返回值
end_time = time.time()
print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
return inner_ret # 将调用原函数的返回值返回给调用者
return inner # 闭包,返回内层函数的引用
@timer # show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info= timer(show_info)的简写,官方叫做语法糖
def show_info(name):
print(f"我的姓名是:{name}")
time.sleep(2)
return 'show_info函数的返回值'
ret = show_info('张三') # 相当于ret = inner('张三')
print(ret)
我的姓名是:张三
装饰器打印执行时间为:2.00031
show_info函数的返回值
标准装饰器
由于被装饰函数的参数个数不确定,所以我们在设计装饰器时,内部函数的参数个数就无法确定,因此要设计成不定长的形式
例1:
import time
def timer(func):
def inner(*args, **kwargs): # 设计时需要添加参数
start_time = time.time()
inner_ret = func(*args, **kwargs) # 调用原函数indxe,传入参数并接受返回值
end_time = time.time()
print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
return inner_ret # 将调用原函数的返回值返回给调用者
return inner # 闭包,返回内层函数的引用
@timer # show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info= timer(show_info)的简写,官方叫做语法糖
def show_info(name, age):
print(f"我的姓名是:{name}, 今年{age}岁")
time.sleep(2)
return 'show_info函数的返回值'
ret = show_info('张三', 25)
print(ret)
我的姓名是:张三, 今年25岁
装饰器打印执行时间为:2.0017
show_info函数的返回值
特别注意
在标准装饰器中,def inner(*args, **kwargs):
中的 *
在函数 定义 的时候是将传入函数的参数聚合成一个元素,在 调用函数 inner_ret = func(*args, **kwargs)
的时候 *
的作用是将一个可迭代的对象进行打散,即将变量 args或kwargs
打散,拆分成一个个要传入的实参
标准装饰器模板:
def decorator(real_func_name):
def inner(*args, **kwwargs):
'''调用装饰器访问函数前要执行的操作'''
ret = real_func_name(*args, **kwwargs)
'''调用装饰器访问函数后要执行的操作'''
return ret
return inner
装饰器的应用
装饰器一般用于登录验证和日志
例1:
def login():
name = input('请输入用户名:')
pwd = input('请输入密码:')
login_status['name'] = name
if pwd == '123456':
login_status['status'] = True
return 1
else:
return 0
login_status = {
'name': None,
'status': False
}
def decorator(func_name):
def inner(*args, **kwargs):
if login_status['status']:
ret = func_name(*args, **kwargs)
return ret
else:
ret = login()
if ret:
ret = func_name()
return ret
else:
print('登录失败')
return inner
@decorator
def index():
print("index页面")
@decorator
def logger():
print("logger页面")
# 只有登录成功后才会执行函数,并打印信息
index()
logger()
请输入用户名:张三
请输入密码:123456
index页面
logger页面
带参数的装饰器
如果给不同的函数在调用装饰器的时候,给出不同的日志记录文件,那么就需要在装饰函数的时候,传入一个装饰器路径参数
例1:
import datetime
import os
# 带参数的装饰器
def logger(file):
def outer(func):
def inner(*args, **kwargs):
# 如果目录不存在创建目录,并修改目录权限
dir_path = '/projects/logger/'
if not os.path.exists(dir_path):
os.makedirs(dir_path)
os.chmod(dir_path, mode=0o777)
print('文件夹不存在已自动创建')
# 执行被装饰的函数
ret = func(*args, **kwargs)
# 将创建文件信息写入文件
with open(os.path.join(dir_path, file), mode='a+',encoding='utf-8') as f:
dt_ms = datetime.datetime.now()
f.write(f'于 {dt_ms} 调用了 {func.__name__}\n')
return ret
return inner
return outer
# 带参数的装饰器
@logger('goods.log')
def goods():
print('购买商品')
# 带参数的装饰器
@logger('login.log')
def login():
print('登录')
# 带参数的装饰器
@logger('order.log')
def order():
print('下单')
if __name__ == '__main__':
for i in range(5):
goods()
login()
order()
运行结果:
[root@localhost logger]# cat login.log
于 2020-08-18 11:56:44.415420 调用了 login
于 2020-08-18 11:56:44.415650 调用了 login
于 2020-08-18 11:56:44.415873 调用了 login
于 2020-08-18 11:56:44.416067 调用了 login
于 2020-08-18 11:56:44.416260 调用了 login[root@localhost logger]# cat order.log
于 2020-08-18 11:56:44.415511 调用了 order
于 2020-08-18 11:56:44.415745 调用了 order
于 2020-08-18 11:56:44.415944 调用了 order
于 2020-08-18 11:56:44.416126 调用了 order
于 2020-08-18 11:56:44.416364 调用了 order[root@localhost logger]# cat goods.log
于 2020-08-18 11:56:44.415233 调用了 goods
于 2020-08-18 11:56:44.415586 调用了 goods
于 2020-08-18 11:56:44.415813 调用了 goods
于 2020-08-18 11:56:44.416007 调用了 goods
于 2020-08-18 11:56:44.416199 调用了 goods