python中的装饰器二(带参数的装饰器)

2023-05-16

文章目录

        • 前言
    • 一 被装饰函数带参数
    • 二 被装饰函数有返回值
    • 三 装饰器带参数
    • 四 多个装饰器

前言

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python中的装饰器二(带参数的装饰器) 的相关文章

  • 一阶方向导数与梯度和方向向量的关系及其应用

    一 基本概念 1 方向导数 xff08 Directional derivative xff09 方向导数是指在给定点沿着某个方向的导数 xff0c 表示函数在该方向上的变化率 具体而言 xff0c 对于一个向量场 f x
  • 机器人中的数值优化(一)—— 数学优化、凸集合与凸函数

    本系列文章主要是我在学习 数值优化 过程中的一些笔记和相关思考 xff0c 主要的学习资料是深蓝学院的课程 机器人中的数值优化 和高立编著的 数值最优化方法 等 xff0c 本系列文章篇数较多 xff0c 不定期更新 xff0c 上半部分介
  • ROS主机搭建NFS服务器,虚拟机通过挂载访问及修改主机文件

    本文主要介绍在ROS主机中搭建NFS服务器 xff0c 虚拟机 xff08 从机 xff09 通过nfs挂载的方式访问及修改主机中文件的方法 一 ROS主机NFS服务器搭建 xff1a 若机器人配有显示屏 xff0c 此部分可直接在机器人上
  • ST-LINK V2.1 制作(含源码及其原理图)(type-c接口)可以配合robomaster 开发板下载口或者直接用杜邦线连接下载,支持串口调试

    标题ST LINK V2 1 制作 xff08 含源码及其原理图 xff09 xff08 type c接口 xff09 可以配合robomaster 开发板下载口或者直接用杜邦线连接下载 xff0c 支持串口调试 基于电子爱好者 xff0c
  • Java实现快速排序

    快速排序是在每一轮挑选一个基准元素 xff0c 比他大地站左边 xff0c 比他小的站右边 xff0c 从而把数列拆分成两部分 xff0c 假如元素是n个 xff0c 平均情况下需要logn轮 xff0c 因此平均实践复杂度是O nlogn
  • typora修改偏好设置不生效/无法修改偏好设置/偏好设置被重置的解决方法

    原因 之前bata版本过期 许多人都尝试修改电脑时间来继续使用 因此对 profile data这个文件的权限设置不正确 步骤 电脑勾选显示隐藏文件 C Users 电脑名 AppData Roaming Typora 选择时间排序 找到该
  • 单片机控制小车前进转弯

    1 小车前进 实现方法 xff1a 四个电机同时向前 xff08 小车后退同理 xff09 include lt AT89X51 H gt 定义小车驱动模块输入IO口 sbit IN1 61 P1 0 右1电机 高电平前进 sbit IN2
  • Java线上应用故障排查之一:高CPU占用

    一个应用占用CPU很高 xff0c 除了确实是计算密集型应用之外 xff0c 通常原因都是出现了死循环 xff08 友情提示 xff1a 本博文章欢迎转载 xff0c 但请注明出处 xff1a hankchen xff0c http www
  • 树莓派3B安装win11操作系统-成功版

    三个注意事项 事前准备永恒的决心成功 61 失败 事前准备 1 树莓派3B主板及电源连接 xff1b 2 HDMI的显示器 xff1b 3 USB的键盘鼠标 xff0c 或者有USB连接器的键盘鼠标 永恒的决心 1 其他安装过程 xff0c
  • 树莓派3B+安装Android 系统

    试水在树莓派3B 43 上安装Android 系统 xff08 完整的安卓 xff0c 非Adroid IOT xff0c 因为还不支持 xff09 xff0c 受到了一个小哥安装成功的鼓舞 xff08 带度盘资源 xff1a https
  • WinDbg 的入门经历

    WinDbg 的入门经历 WinDbg 是一款非常好用的调试工具 xff0c 针对于在win上的程序都有很好的调试效果 xff0c 相信玩逆向工程 Net 开发的同学都很熟悉 起因 在调试UCOS操作系统在windows上的仿真问题遇到的多
  • 目标检测网络中的 bottom-up 和 top-down理解

    看目标检测网络方面的论文时 xff0c 出现了一组对比词汇 xff1a bottom up和top down xff0c 查了一些资料 xff0c 结合个人理解 xff0c 得到的看法是 xff1a top down 顾名思义是自上而下进行
  • 2021.08.26学习内容 Win10+GeForce GTX1650安装NVIDIA显卡驱动及CUDA11.4+cuDNN8.2

    之前主要使用Ubuntu系统 xff0c 但是个人笔记本更多使用windows 为了方便跑一些pytorch的小代码 xff0c 所以想在windows配置一下相关环境 xff0c 达到调用GPU运算的目的 记录也是为了自己以后有安装需求少
  • vscode调试webpack-dev-server项目

    先上结果 vscode下载debugger for chrome 插件 创建launch json 添加的时候选择Chrome Launch会自动生成chrome调试模板 xff0c 主要是要加上 34 preLaunchTask 34 3
  • OpenNetworkLinux:i2c-gpio.c源码学习笔记

    OpenNetworkLinux xff1a i2c gpio c源码学习笔记 i2c gpio的init和exit i2c驱动需要首先在平台驱动上进行注册 xff0c 方可提供自身的总线供适配器进行注册 xff0c 注册流程类似于一个内核
  • 每天一个Lodash源码解析

    每天一个Lodash源码解析 chunk 方法介绍自我实现源码分析代码对比知识点补充浮点数转化为浮点数数组创建方法区别js中切割数组方法 slice 方法介绍自我实现源码分析代码对比知识点补充 96 96 gt gt gt 96 96 移位
  • conda的常用操作

    1 查看conda版本 2 更改安装第三方库的源 将国外的源改为清华镜像源 cmd窗口依次输入 xff1a conda config add channels https mirrors tuna tsinghua edu cn anaco
  • 云技术

    什么是 云 与云技术 xff1f 云技术是在分布式计算技术 网格计算基数基础上发展起来的一种计算技术 云技术基于资源虚拟化的方式 xff0c 为用户提供方便快捷的服务 xff0c 可以实现计算与存储的分布式与并行处理 云技术为其他信息技术
  • 什么是上转型对象及其基本特征

    5 12 什么是上转型对象及其基本特征 xff1f 上转型对象 子类对象赋值给父类变量 例如 xff1a package com wangxing test1 父类 public class Person public void testP
  • 【树莓派入门系列】4 树莓派安装测试torch与torchvision

    树莓派运行YOLOV5项目 安装测试torch amp torchvision 项目目录 点击跳转 0 首页 1 前期准备工作 2 配置树莓派系统 3 安装测试opencv 4 安装测试torch amp torchvision 5 测试y

随机推荐