IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> python中的装饰器二(带参数的装饰器) -> 正文阅读

[Python知识库]python中的装饰器二(带参数的装饰器)

前言

看大佬们写的代码看的我是眼花缭乱,不知所云啊。真是感叹,自己还看都看不懂,人家就能写出来了,人与人之间的差距确实有点大呀,为了能够早些看懂大佬们的代码,继续薅头发吧~
阅读这篇可能需要点装饰器基础知识,可以先参考这篇:python中的装饰器(基础装饰器)

一 被装饰函数带参数

在上篇 python中的装饰器(基础装饰器) 中讲的装饰器和被装饰函数都是没有带任何参数的,在实际编程中往往有很多被装饰函数和装饰器都是带有参数的,所以了解它们的带参数情况是很有必要的。下面先从被装饰函数带参数开始,如下。

下面仍然用函数装饰器实现 :计算函数的运行时间 的功能,如下

#被装饰函数带参数
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate():
        start_time = time.time()
        func()
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

    return decorate

@timer
def func():
    time.sleep(2)

func()

上面就用timer装饰器实现了计算函数运行时间的功能,此时被装饰函数func是没有参数的。
如果把func函数延迟的时间由传入参数决定,那么func函数就变成了下面这样

def func(num):
    time.sleep(num)

此时被装饰函数func 带了一个参数num,那么装饰timer又该如何变动呢?timer的变动如下

#被装饰函数带参数
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate(num):
        start_time = time.time()
        func(num)
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

    return decorate

@timer
def func(num):
    time.sleep(num)

func(3)
>>>
函数运行时间: 3.0008623600006104

与上面相比,这里的装饰器timer 有了些变化:decorate()函数和func()函数都多了一个形参num, 这就是当被装饰函数带有参数时装饰器的写法,装饰函数和被装饰函数也应该有对应的形参变量。

func() 添加对应的形参变量我能理解,因为它本身就是被装饰函数,但是装饰函数 decorate() 为什么也要增加形参变量呢?当时这里想了很久都没有想明白(好菜),网上搜索也没有讲的这么详细的,最后在某天就想着把它拆开一步步分析,分析过程如下

  • 1. @timer 还原就是func=timer(func) ,执行timer的时候,timer首先接受一个func变量,然后执行decorate() 函数,decorate() 函数里面调用func(),也就是被装饰函数本身func(),所以decorate()里面的func也要增加对应的形参变量。
  • 2. 当timer装饰完成后,返回decorate,即func=decorate,当执行func(3)的时候带了一个实参3,执行func(3)就相当于执行decorate(3),所以decorate()自然也得有对应的形参变量来接受,并且提供给decorate()里面的func使用。

标准的装饰器timer写法是下面这样的,以应对被装饰函数func不确定的传参

#被装饰函数带参数
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate(*args, **kargs):
        start_time = time.time()
        func(*args, **kargs)
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

    return decorate

@timer
def func(num):
    time.sleep(num)

#func=timer(func)
func(2)

>>>
函数运行时间: 2.0000545978546143

二 被装饰函数有返回值

当被装饰函数有返回值时,我们可以在装饰函数里面用一个变量接受,如下

#被装饰函数有返回值
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate(*args, **kargs):
        start_time = time.time()
        #用变量res接受func的返回值
        res = func(*args, **kargs)
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

        return res

    return decorate

@timer
def func(num):
    time.sleep(num)

    return num

print("func的返回值:", func(2))

>>>
函数运行时间: 2.000554084777832
func的返回值: 2

上面用变量res接受func的返回值,然后return res 返回给外面用。

三 装饰器带参数

对于有参数的装饰器,我们需要用两层闭包来写,如下

#装饰器带参数
import time
from functools import wraps

def my_timer(parm):

    def timer(func):

        @wraps(func)
        def decorate(*args, **kwargs):
            if parm == 1:
                print("这是func1")
                start_time = time.time()
                func(*args, **kwargs)
                end_time = time.time()
                total = end_time - start_time
                print("函数运行时间:", total)
            elif parm == 2:
                print("这是func2")
                start_time = time.time()
                func(*args, **kwargs)
                end_time = time.time()
                total = end_time - start_time
                print("函数运行时间:", total)

        return decorate

    return timer

@my_timer(1)
def func1():
    time.sleep(1)

@my_timer(2)
def func2():
    time.sleep(2)

func1()
func2()
>>>
这是func1
函数运行时间: 1.0001475811004639
这是func2
函数运行时间: 2.0003774166107178

这里的timer比上面的timer多了一层外层函数my_timer,它的作用就是和timer构成一个闭包,然后timerdecorate构成一个闭包。
在这里插入图片描述

因为装饰函数decorate引用了两个自由变量:parm和func,而它的外层函数timer只能传一个func,timer自然需要外加一层函数来传parm变量,并且timer的外层函数要与timer构成闭包,以让接受的parm变量成为自由变量,从而提供给装饰函数decorate使用。
以上就是装饰器带参数的写法。

四 多个装饰器

有时候一个装饰器还不能满足需求,要多个装饰器一起才能实现,多个装饰器的写法很简单,难的是理解它的执行顺序,如下

#多个装饰器
def deco_one(func):

    print("这是deco_one")
    def deco1(*args, **kwargs):
        print("这是deco1的开始")
        func(*args, **kwargs)
        print("这是deco1的结束")

    return deco1

def deco_two(func):

    print("这是deco_two")
    def deco2(*args, **kwargs):
        print("这是deco2的开始")
        func(*args, **kwargs)
        print("这是deco2的结束")

    return deco2

@deco_one
@deco_two
def func():
    print("Hello world")

func()

先说下多个装饰器的执行顺序:

  • 1.不附加在装饰函数里面的是自底向上执行
  • 2.在装饰函数里面的,但在被装饰函数func()之前的是自顶向下执行
  • 3.执行被装饰函数func()
  • 4.在装饰函数里面的,并且在被装饰函数func()之后的是自底向上执行

所以上面的执行结果是

>>>
这是deco_two
这是deco_one
这是deco1的开始
这是deco2的开始
Hello world
这是deco2的结束
这是deco1的结束

在这里插入图片描述

python装饰器带参数的就到此结束了,上篇文章:python中的装饰器(基础装饰器)

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:52:04  更:2022-10-31 11:53:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/26 3:46:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计