《python进阶》之装饰器理解

前言

😵😵学到后面越来越难了😫😭,什么装饰器,类,模块,对象,线程看到头大一圈,对于没有计算机基础的我生啃确实有点难,好在大多数问题看多几篇CSDN大神讲解都能看懂,就剩下实战熟练了。

一、什么是装饰器?

👉👉口水理解:简单从字面上去理解,就是一个"锦上添花"的东东,这种锦上添花是不会影响你原来的功能和调用方法的,不同于真正的"锦上添花",这种锦上添花非常非常有用,就想你的笔记本电脑上的键盘膜,有了它也不影响笔记本的功能,但是又多了一个保护键盘的功能,跟手机壳是一个道理的。
👉👉装饰器的三大特性:

1️⃣ 不改变原函数的代码
2️⃣ 不改变原函数的调用方式
3️⃣ 装饰器返回的是函数

二、简易装饰器

✋✋“存在就是合理”,所以我们需要去理解它存在的意义。
首先要理解一个概念或者一种做法,就是函数也是可以作为另外一个函数的参数进行传参运行的。
看下面的栗子🌰:

import time
def printName():
    print('I am 峰勇力.')
    time.sleep(1) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的

def control(func):
    print('control函数在运行。')
    func()
    print('printName被包围了。')
    time.sleep(1)
if __name__ == '__main__':
    control(printName)  # 将函数printName传参给函数control
# 输出
control函数在运行。
I am 峰勇力.
printName被包围了。
>>> 

理解了这个操作,那下面才能更好理解。
现在来一个简单的模拟装饰器写法:
看例子🌰:

# 需求:计算函数printName的运算时间。
# 方法1:不用装饰器的思维
import time
def printName():
    print('I am 峰勇力.')
    time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
if __name__ == '__main__':
	t1 = time.time()
    printName()
    print('运行时间:{}'.format(time.time() - t1))
# 输出
I am 峰勇力.
运行时间:2.027989625930786
>>> 

上面的方法确实可以解决计算函数的运行时间问题,但是你再仔细想想,这只是一个函数,实际情况会比这个复杂很多,让你计算1000个函数的运行时间并比较优化,可以想象方法1代码会冗余而且不够精简,所以衍生出装饰器这个想法的解决办法(我看到的那刻真的觉得太绝了👍👍👍)。
看例子🌰:

# 需求:计算函数printName的运算时间。
# 方法2:简陋的装饰器的思维,不加语法糖
import time
def printName():
    print('I am 峰勇力.')
    time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
    
def timer(func):
    def wrapper():
        t1 = time.time()
        func()
        print('运行时间:{}'.format(time.time() - t1))
    return wrapper

if __name__ == '__main__':
    printName = timer(printName) # <1>
    printName() # <2>
# 输出
I am 峰勇力.
运行时间:2.032205581665039
>>> 

  这里需要解释一下的是在 <1> 处的运行思路和顺序:
顺序:printName函数 -> wrapper函数 -> printName 这个存储空间;
逻辑:**首先 timer 函数将 printName函数 作为参数传入,然后我们只看 返回(return) ,返回的是wrapper函数,然后将 wrapper函数 命名为 printName 这个空间,实际上现在 <2> 处的printName()调用的就是 wrapper 函数,实际上运行的就是wrapper函数,但是呢,你又把原始的printName函数传给timer当参数了,所以wrapper函数还是会调用函数printName(),有点像是自己调用自己的感觉。😛😛

三、装饰器运用场景(为啥要用)

  从上面的演示作用来看,装饰器这种思维是很有用处的,最大的好处就是你原来的代码完全可以不用做修改,就可以知道某一模块的运行情况和获取日志,装饰器的主要功能也是如此,比如插入日志、性能测试、事务处理、缓存、权限校验等场景也是用装饰器来实现的(网上抄的)。😉😉

四、装饰器语法

(一)没有参数的装饰器

  上面是没有用装饰器语法糖的写法(我也不知道为啥叫语法糖)😅,大概网上的说法python的语法糖是 **@**这个符号,请看下面用语法糖的栗子🌰:

# 方法3:使用语法糖@
import time
def timer(func):
    def wraper():
        t1 = time.time()
        func()
        print('运行时间:{}'.format(time.time() - t1))
    return wraper
@timer  # <3>
def printName():
    print('I am 峰勇力.')
    time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
printName()
# 输出
I am 峰勇力.
运行时间:2.0204033851623535

  可以看到输出的结果跟之前的 方法2的效果是一样的,唯一的区别就是<3>处的 @timer和<1>处的printName = timer(printName),所以从形式上看,其他都一样就这两个不一样,但是结果又是一样的,说明 @timer 和**printName = timer(printName)**是等价的,这样你就明白装饰器的最基础的用法和原理了。

(二)带参数的装饰器

  这个可能有点点绕😅😅,直接看栗子吧🌰:

# 方法4:装饰器带参数
import time
def timer_out(process):
    print(process)
    def timer(func):   # <2>
        print('正在调用装饰器timer')
        def wraper(*args,**kwargs):
            t1 = time.time()
            func(*args,**kwargs)
            print('运行时间:{}'.format(time.time() - t1))
        return wraper
    # 返回的才是装饰器,记住这个写法。
    return timer  
@timer_out('调用外围函数timer_out') # <1>
def printName(name):
    print('I am {}'.format(name))
    time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
printName('峰勇力')
# 输出
调用外围函数timer_out
正在调用装饰器timer
I am 峰勇力
运行时间:2.000643730163574

  理论上来说,装饰器传入的参数必须是要函数,然后返回是函数,但是也不知道python哪个大神这个想法,竟然用函数带参数然后返回装饰器再用装饰器的用法,妥妥的俄罗斯套娃😅😅
  看 <1> 处,函数 timer_out 带了一个参数,这样看上去实际上不能算真正意义上的装饰器,但是timer_out函数返回的却是一个装饰器,后面就是完全按照装饰器的用法运行了,所以它的执行顺序就比较明显了:timer_out --> timer(func)(把函数printName传入) --> wraper(*args,**kwargs) ,因为设置了可以传入任意参数,所以wraper把参数 ‘峰勇力’ 传给printName函数,这样就是实现不同参数的分别传入,也是实现了装饰器的带参数使用。

五、有什么说的不正确的希望跟我说一下😅😅,小学鸡原谅一下

OVER~
在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峰勇力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值