Python学习第四十天—函数6(装饰器)

[复制链接]
15271953841 发表于 2024-3-4 12:37:04 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

×
本帖最后由 15271953841 于 2024-3-4 12:40 编辑

Python 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
一、        回顾上节课内容
总结:1、利用嵌套函数的外层作用域具有记忆能力的这个特性。
2、将内层函数作为返回值给返回
提出新的问题:能不能把一给函数作为参数传递给另一个函数呢?

def myfunc():                    (测试函数)
    print("正在调用myfunc...")

   
def report(func):                   (调用函数,参数是一个函数的引用)
    print("主人,我要开始调用函数了...")
    func()                     (函数名其实就是函数的引用,加了小括号就是调用)
    print("主人,我调用完函数啦,快夸夸我^O^")

                  
report(myfunc)          (要调用myfunc,不直接调用它,而是把它作为参数,传递给report()函数)
主人,我要开始调用函数了...
正在调用myfunc...
主人,我调用完函数啦,快夸夸我^O^

import time                  (时间管理大师)
def time_master(func):
    print("开始运行程序...")
    start=time.time()
    func()
    stop=time.time()
    print("结束程序运行...")
    print(f"一共耗费了{(stop-start):.2f}秒")

   
def myfunc():                  (测试函数)
    time.sleep(2)
    print("Hello little sister.")  
              (怎么去统计myfunc这个函数的运行时间呢?不直接调用,调用它没有用,我们是调用time_master,把myfunc这个函数作为参数传递到time_master()中。)
   
time_master(myfunc)             (调用函数)
开始运行程序...
Hello little sister.
结束程序运行...
一共耗费了2.04秒

二、更好的方案是在调用myfunc()函数时候,它能够自觉的执行time_master()函数,是呀,学会了Python的装饰器,就可以了。
编辑模式下:
import time

def time_master(func):
    def call_func():
        print("开始运行程序...")
        start=time.time()
        func()
        stop=time.time()
        print("结束程序运行...")
        print(f"一共耗费了{(stop-start):.2f}秒。")
    return call_func
                   (这就是装饰器的魔力)
@time_master
def myfunc():
    time.sleep(2)
    print("I love Little sister.")

myfunc()
上面的程序,在编辑模式下点击“file”中的“save”保存后,再点击“run”中的“run module”后,在互动模式下的运行效果:
=================== RESTART: D:/谷兴武/创客/python学习/时间管理大师装饰器.py ===================
开始运行程序...
I love Little sister.
结束程序运行...
一共耗费了2.04秒。

理解一下装饰器到底是怎么实现的呢?它的原理、它的本质是什么呢?认真看代码,那不就是闭包吗,还有前面刚介绍的新知识:拿函数当参数,综合起来就是装饰器。大家看到这个@加装饰器的名字:@time_master,其实这是一个语法糖,装饰器原来的样子应该是这样的,看下面修改后的程序:
import time

def time_master(func):
    def call_func():
        print("开始运行程序...")
        start=time.time()
        func()
        stop=time.time()
        print("结束程序运行...")
        print(f"一共耗费了{(stop-start):.2f}秒。")
    return call_func
这个函数是闭包,当我们去调用time_master()这个函数的时候,并不会去执行内部的call_func(),而是将它返回。

def myfunc():
    time.sleep(2)
    print("I love Little sister.")
那下面这调用了time_master并且把myfunc给传递进去,所以它这时候并不会调用,而是返回它内部的call_func,把这个内部call_func赋值给这个myfunc.
@time_master是装饰器的语法糖,相当于后面我们在调用myfunc的时候,并不是直接调用myfunc,而是把myfunc这个函数作为一个参数塞到上面这个装饰器里面,然后去调用这个装饰器。

myfunc=time_master(myfunc)

myfunc()                 (那么下面我们调用这myfunc的时候,实际上是调用了call_func)

程序运行效果是一样的
=================== RESTART: D:/谷兴武/创客/python学习/时间管理大师装饰器.py ===================
开始运行程序...
I love Little sister.
结束程序运行...
一共耗费了2.03秒。

三、        那么,多个装饰器能否同时用在一个函数上呢?
编辑模式下写好下面的程序,保存为.py文件

def add(func):
    def inner():
        x=func()
        return x+1
    return inner

def cube(func):
    def inner():
        x=func()
        return x*x*x
    return inner

def square(func):
    def inner():
        x=func()
        return x*x
    return inner

@add
@cube
@square
def test():
    return 2

print(test())
运行后在交互模式下查看运行效果:
=================== RESTART: D:/谷兴武/创客/python学习/装饰器3个装饰器测试.py ==================
65
可以看出调用顺序是:先调用square(),再调用cube(),最后调用add(),这就是同时使用多个装饰器的方法,以及了解它的调用顺序。

四、最后,如何给装饰器传递参数呢?
import time
def logger(msg):
    def time_master(func):
        def call_func():
            start=time.time()
            func()
            stop=time.time()
            print(f"[{msg}]一共耗费了{(stop-start):.2f}")
        return call_func
    return time_master

@logger(msg="A")
def funA():
    time.sleep(1)
    print("正在调用funA...")

@logger(msg="B")
def funB():
    time.sleep(1)
    print("正在调用funB...")

funA()
funB()

================== RESTART: D:/谷兴武/创客/python学习/装饰器highlevel.py =================
正在调用funA...
[A]一共耗费了1.06
正在调用funB...
[B]一共耗费了1.04
上面的程序中,装饰器带有参数,而这个参数是如何实现的呢?我们看一下上面定义的部分,多加了一层嵌套,现在我们把它们的两个语法糖拆掉,写成它们传统的样子:
import time
def logger(msg):
    def time_master(func):
        def call_func():
            start=time.time()
            func()
            stop=time.time()
            print(f"[{msg}]一共耗费了{(stop-start):.2f}")
        return call_func
    return time_master


def funA():
    time.sleep(1)
    print("正在调用funA...")


def funB():
    time.sleep(1)
    print("正在调用funB...")


funA = logger(msg="A")(funA) (最后要覆盖回原本的函数名的,所以写成funA =,logger是装饰器最外层,装饰器最外层需要你传递一个msg,第一给传递的是A,logger(msg="A")得到的time_master这个函数的引用,但是我们不要time_master引用,而是要call_func引用,那么我们应该再调用一次,调用time_master得到是call_func,这里传入的函数的名字funA,这样就是它原来的样子funA = logger(msg="A")(funA)
funB = logger(msg="B")(funB)

funA()
funB()
运行效果一样:
=============== RESTART: D:/谷兴武/创客/python学习/装饰器highlevel原来的样子.py ===============
正在调用funA...
[A]一共耗费了1.03
正在调用funB...
[B]一共耗费了1.05

关注公众号

相关侵权、举报、投诉及建议等,请发 E-mail:admin@discuz.vip

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.|鄂ICP备13007585号-4

在本发帖
关注公众号
QQ客服返回顶部