python之装饰器进阶

开场白

昨天讲了装饰器的定义、入门、多重以及伪装,今天的内容是装饰器的进阶。学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
密码错误!

python之装饰器进阶

测试结果完全满足客户的需求,实现了周一至五不记录交易log,周六日记录交易log。

带参数装饰器详解

直接读上面大段代码对于装饰器运用不熟练的朋友可能会有些懵懂,下面详细讲解带参数装饰器。

带参数装饰器至少有3层结构,即最少包含3层def和3层return。

  1. 第一层:负责接收装饰器自身的参数,再返回第二层函数。
  2. 第二层:负责接收被装饰的函数,再返回第三层函数。
  3. 第三层:这一层做的事情很多,按功能划分为3块。
    1. 负责接收被装饰函数的参数。
    2. 对装饰器自身的参数进行解析处理,若其满足某条件则做某动作,不满足条件则不做动作(或做别的动作)。
    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,运行时间时间需要多少秒?

python之装饰器进阶

运行效率相差21倍!!!

在此鼓励一下学python的朋友,不要以为python运行效率低,其实深入学好python,学会各种优化方法可以大幅度提升运行效率。

上一篇:二分4——2021-02-04更


下一篇:短信验证码接入----腾讯云短信