开场白
昨天讲了装饰器的定义、入门、多重以及伪装,今天的内容是装饰器的进阶。学python的朋友一定要先看闭包、再看装饰器、最后再看装饰器的进阶。
带参数装饰器是关于装饰器的最后一个难点,有疑惑可以留言探讨,希望大家都能掌握好关于装饰器的知识点。
带参数装饰器结构
大家都知道了装饰器是用来拓展函数功能的,但是别忘了装饰器本身也是函数,当然也可以通过给装饰器增加参数来拓展功能。
我们继续昨天的案例讲解带参数装饰器:现在客户又提需求了,客户发现周一到周五收银机的帐目很清楚,但是周六日账目有点混乱。客户希望收银机把周六和周日的所有交易记录保存到日志文件中,周一到周五的交易记录不需要保存。
分析客户需求,按照我们昨天学习的装饰器的内容,可以很快想到解决方案,再写一个装饰器用来保存交易记录。但是这里有一个难点,客户要求周一至周五的不保存,周六周日的保存,怎么办呢?
在这里就需要用到带参数装饰器了,具体代码如下:
import functools
from datetime import datetime
# 这行代码是获取当前是星期几,周一对应对应1,周六对应6,周日对应7
# day_week = datetime.now().isoweekday() # 今天是周二,明天周三,不会触发记录日志的条件,暂注释,方便测试
day_week = 7 # 为了测试,假定今日是周日。实际使用时注释这行,取消注释上面一行。
def check_week(chk):
def inner(func):
@functools.wraps(func)
def inner_chk(*args, **kwargs):
if chk:
with open('log.txt', mode='a', encoding='utf8') as f:
f.write(f'交易记录:折扣值是{args[0]},商品单价{args[1]},商品数量{args[2]},交易时间是{datetime.now()}\n')
return func(*args, **kwargs)
return inner_chk
return inner
def checkdisct(func):
@functools.wraps(func)
def inner(*args, **kwargs):
disct = args[0]
if disct >= 0.5 and disct <= 1:
print('折扣值合理!')
return func(*args, **kwargs)
else:
print('折扣值不合理!')
return inner
def checkpwd(func):
@functools.wraps(func)
def inner(*args, **kwargs):
pwd = input('请输入密码:')
if pwd == "123456":
print("密码正确!")
return func(*args, **kwargs)
else:
print('密码错误!')
return inner
# 判断不是星期六和日则设置day_week为0,不触发记录log;若是周六、周日会触发记录log。
if day_week != 6 or day_week != 7:
day_week = 0
@checkpwd
@checkdisct
@check_week(day_week)
def count(x, prince, number):
'''功能:计算商品应付款和实付款的函数。
参数:x是float型,指定折扣额度;prince是float型,指定商品的单价;number是int型,指定商品的数量。'''
result = prince * number
pay = result * x
print(f'总价是{result}元,实付{pay}元')
count(0.8, 2.88, 100)
count(0.3, 2.88, 100)
out:
请输入密码:123456
密码正确!
折扣值合理!
总价是288.0元,实付230.4元
请输入密码:1
密码错误!
测试结果完全满足客户的需求,实现了周一至五不记录交易log,周六日记录交易log。
带参数装饰器详解
直接读上面大段代码对于装饰器运用不熟练的朋友可能会有些懵懂,下面详细讲解带参数装饰器。
带参数装饰器至少有3层结构,即最少包含3层def和3层return。
- 第一层:负责接收装饰器自身的参数,再返回第二层函数。
- 第二层:负责接收被装饰的函数,再返回第三层函数。
- 第三层:这一层做的事情很多,按功能划分为3块。
- 负责接收被装饰函数的参数。
- 对装饰器自身的参数进行解析处理,若其满足某条件则做某动作,不满足条件则不做动作(或做别的动作)。
- 返回被装饰的函数及其参数。
def check_week(chk): # 带参数装饰器,chk是判断条件,chk不是0则记录交易日志,chk是0则不记录
def inner(func): # func是被装饰的函数名
@functools.wraps(func)
def inner_chk(*args, **kwargs): # *args和**kwargs是被装饰的函数参数
if chk: #判断条件是否满足,若不为0则将交易记录和交易时间保存到log.txt文件
with open('log.txt', mode='a', encoding='utf8') as f:
f.write(f'交易记录:折扣值是{args[0]},商品单价{args[1]},商品数量{args[2]},交易时间是{datetime.now()}\n')
return func(*args, **kwargs) # 返回被装饰的函数及其参数
return inner_chk # 返回第三层函数inner_chk
return inner # 返回第二层函数inner
以上案例详细讲解了带参数装饰器的构造,要学习装饰器必须多加练习才能真正掌握。
带参数装饰器的运用场景
关于带参数装饰器的运用场景有很多,比较常见的有是否记录业务日志,是否生成性能日志,使用测试数据库运行或使用生产数据库运行等等,具体要因业务需求而定。
装饰器的补充运用
关于装饰器的运用,除了可以自己写的装饰器用,也有一些官方或第三方提供的装饰器非常好用。例如之前有提及的@functools.wraps装饰器。
这里再补充一个numba的jit装饰器,功能是通过即时编译的方法提升python函数运行效率。在涉及大量循环运算的场合建议尝试一下。以下是测试代码。
from numba import jit
import time
@jit
def fib(n):
if n <= 2:
return 1
else:
return fib(n - 1) + fib(n - 2)
start = time.time()
print(fib(40))
end = time.time()
print(f"用时:{end - start}秒")
out:
102334155
用时:1.001574993133545秒
大家猜猜如果注释掉@jit,运行时间时间需要多少秒?
运行效率相差21倍!!!
在此鼓励一下学python的朋友,不要以为python运行效率低,其实深入学好python,学会各种优化方法可以大幅度提升运行效率。