python @ 装饰器(修饰器,语法糖)使用与不使用对比总结记录

news2024/11/18 5:58:57

由于看python代码里面函数上的@ 不爽很久了,为了避免自己又忘记了这里来记录下。
简单总结:
@ 的作用就是在使用 @ 下面的函数(如下图的cs2)的时候,会在该函数执行前将该函数作为参数扔到@后跟着的处理函数先行处理(或预备处理)些东西,如下图的 time_cs 就是本文举的封装一个计时函数的装饰器的日志。

阅读下面代码对比使用语法糖的使用,即可了解。
本篇博客自参考b站大佬up的视频
在这里插入图片描述
在这里插入图片描述

一.预备的python常识

1.python中函数加括号与不加返回的区别

区分a()和a如下
一个是: 函数a在电脑中的十六进制地址
一个是: 函数a返回的值

def a():
    return 1

print(a) # 函数a在电脑中的十六进制地址
print(a()) # 函数a返回的值

输出

<function a at 0x000001E37E1C6DC8>
1

2.在python中函数可以作为参数直接传入


def a():
    return "a的返回"

def b():
    return "b的返回"

def out(action):
    """传入函数对象"""
    print(action())

out(a)
out(b)

输出

a的返回
b的返回

二. 计时函数的例子说明

1.1 不使用装饰器的写法简单-0(无输入输出)

即检测的目标函数没有输入值也没有返回值的情况

import time

def time_cs(func):
    """

    :param func: 传入的你需要计时的函数
    :return: 无
    """

    print("开始计时,打下时间戳")

    start = time.perf_counter()

    func()

    end = time.perf_counter()

    print("完成计时,消耗时间" + str(end-start))
    print("\n")

def cs():
    print("\n")
    print("*"*8 + "fun" + "*"*8)
    print("只是打印当前信息的测试函数")
    print("*" * 20)
    print("\n")
    time.sleep(0.25)

time_cs(cs)

输出

开始计时,打下时间戳


********fun********
只是打印当前信息的测试函数
********************


完成计时,消耗时间0.2602558

1.2 不使用装饰器的写法简单-1(只有输出)

即目标函数拥有返回值的情况
这里比较麻烦需要在time_cs 函数中将返回值用一个变量存储,使得你需要预先知道目标函数返回值的个数

import time

def time_cs(func):
    """

    :param func: 传入的你需要计时的函数
    :return: func函数的输出
    """

    print("开始计时,打下时间戳")

    start = time.perf_counter()

    num = func()

    end = time.perf_counter()

    print("完成计时,消耗时间" + str(end-start))
    print("\n")
    return num

def cs2():
    print("\n")
    print("*"*8 + "fun2" + "*"*8)
    print("打印当前信息的测试函数,并返回一个整形的10")
    print("*" * 20)
    print("\n")
    time.sleep(0.25)
    return 10

def cs3(num):
    print("cs3函数得到了cs2函数的输出:" + str(num))




cs3(time_cs(cs2))

输出

开始计时,打下时间戳


********fun2********
打印当前信息的测试函数,并返回一个整形的10
********************


完成计时,消耗时间0.257934


cs3函数得到了cs2函数的输出:10

进程已结束,退出代码 0

1.3.不使用装饰器的写法简单-2(有输入有输出)

即目标函数拥有有输入有输出返回值的情况,

1.3.1失败版

首先展示一个能运行但是,无法成功获得目标函数运行时间的写法,这里time_cs输出内部输出由result = func()改成了 result = func

import time


def time_cs(func):
    """
    :param func: 传入的你需要计时的函数
    :return: func函数的输出
    """


    print("开始计时,打下时间戳")

    start = time.perf_counter()

    result = func # 注意这里!!不是 func()!!
    print("传入的函数的id: " + str(id(func)))

    end = time.perf_counter()

    print("完成计时,消耗时间" + str(end - start))
    print("\n")
    return result





def cs2(num):
    print("\n")
    print("*"*8 + "fun2" + "*"*8)
    print("打印当前信息的测试函数,并返回一个整形的" + str(num))
    print("*" * 20)
    print("\n")
    time.sleep(0.25)
    return num

def cs3(num):
    print("cs3函数得到了cs2函数的输出:" + str(num))


num = time_cs(cs2(5))
cs3(num)


输出如下,可以看到该写法下的消耗时间统计是错误的
原因:传入的时候就是传入了一个 cs2(5) 即一个完成了执行了time.sleep(0.25) 之后的程序进入,因此下面输出的9.900000000007125e-06 代表的时间实际就可以理解成输入的func函数运行时间为0时,time_cs函数自身的运行时间

********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************


开始计时,打下时间戳
传入的函数的id: 140720369607200
完成计时,消耗时间9.900000000007125e-06


cs3函数得到了cs2函数的输出:5

1.3.1成功版

import time


def time_cs(func,num):
    """
    :param func: 传入的你需要计时的函数
    :return: func函数的输出
    """


    print("开始计时,打下时间戳")

    start = time.perf_counter()

    result = func(num)
    print("传入的函数的id: " + str(id(func)))

    end = time.perf_counter()

    print("完成计时,消耗时间" + str(end - start))
    print("\n")
    return result





def cs2(num):
    print("\n")
    print("*"*8 + "fun2" + "*"*8)
    print("打印当前信息的测试函数,并返回一个整形的" + str(num))
    print("*" * 20)
    print("\n")
    time.sleep(0.25)
    return num

def cs3(num):
    print("cs3函数得到了cs2函数的输出:" + str(num))


num = time_cs(cs2,5)
cs3(num)

输出

开始计时,打下时间戳


********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************


传入的函数的id: 2832141314952
完成计时,消耗时间0.2586813


cs3函数得到了cs2函数的输出:5

1.3.不使用装饰器的写法简单-4(引入返回函数)

(目标函数拥有输入有输出)
这里将time_cs这个统计输入函数运行时间的功能函数,改造成输入和输出都是函数 的功能函数,这里是最接下面近装饰器写法的,建议与下面的2.使用装饰器的写法对比看

import time
import functools

def time_cs(func):
    """

    :param func: 传入的你需要计时的函数
    :return: func函数的输出
    """
    def my_wrapper(*args, **kwargs):
        """再创建一个装饰器函数"""
        print("开始计时,打下时间戳")

        start = time.perf_counter()

        result = func(*args, **kwargs)
        print("传入的函数的id: " + str(id(func)))

        end = time.perf_counter()

        print("完成计时,消耗时间" + str(end - start))
        print("\n")
        return result

    return my_wrapper

def cs2(num):
    print("\n")
    print("*"*8 + "fun2" + "*"*8)
    print("打印当前信息的测试函数,并返回一个整形的" + str(num))
    print("*" * 20)
    print("\n")
    time.sleep(0.25)
    return num

def cs3(num):
    print("cs3函数得到了cs2函数的输出:" + str(num))


cs2_2 = time_cs(cs2)
cs3(cs2_2(5))
print("传出的函数的id: " + str(id(cs2_2)))
print("传出的函数的名字: " + str(cs2_2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper

输出

开始计时,打下时间戳


********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************


传入的函数的id: 1392449547432
完成计时,消耗时间0.2594437


cs3函数得到了cs2函数的输出:5
传出的函数的id: 1392449545992
传出的函数的名字: my_wrapper

特别的加上 @functools.wraps(func) 以保证传输的函数名字不变

如下

import time
import functools

def time_cs(func):
    """

    :param func: 传入的你需要计时的函数
    :return: func函数的输出
    """
    @functools.wraps(func) # 这里的作用是使得返回的 my_wrapper 的 .__name__ 名字
                            # 和传入的 func.__name__  的相同,如下print(cs2_2.__name__)
    def my_wrapper(*args, **kwargs):
        """再创建一个装饰器函数"""
        print("开始计时,打下时间戳")

        start = time.perf_counter()

        result = func(*args, **kwargs)
        print("传入的函数的id: " + str(id(func)))

        end = time.perf_counter()

        print("完成计时,消耗时间" + str(end - start))
        print("\n")
        return result



    return my_wrapper


def cs2(num):
    print("\n")
    print("*"*8 + "fun2" + "*"*8)
    print("打印当前信息的测试函数,并返回一个整形的" + str(num))
    print("*" * 20)
    print("\n")
    time.sleep(0.25)
    return num

def cs3(num):
    print("cs3函数得到了cs2函数的输出:" + str(num))


cs2_2 = time_cs(cs2)
cs3(cs2_2(5))
print("传出的函数的id: " + str(id(cs2_2)))
print("传出的函数的名字: " + str(cs2_2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper

输出

开始计时,打下时间戳


********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************


传入的函数的id: 2366053213960
完成计时,消耗时间0.2593284


cs3函数得到了cs2函数的输出:5
传出的函数的id: 2366053215688
传出的函数的名字: cs2

进程已结束,退出代码 0

2.使用装饰器的写法(有输入输出)

能够达到以上的效果最接近上面1.3,且使用起来更加的方便

import time
import functools

def time_cs(func):
    """

    :param func: 传入的你需要计时的函数
    :return: func函数的输出
    """
    @functools.wraps(func) # 这里的作用是使得返回的 my_wrapper 的 .__name__ 名字
                            # 和传入的 func.__name__  的相同,如下print(cs2_2.__name__)
    def my_wrapper(*args, **kwargs):
        """再创建一个装饰器函数"""
        print("开始计时,打下时间戳")

        start = time.perf_counter()

        result = func(*args, **kwargs)
        print("传入的函数的id: " + str(id(func)))

        end = time.perf_counter()

        print("完成计时,消耗时间" + str(end - start))
        print("\n")
        return result

    return my_wrapper

@time_cs
def cs2(num):
    print("\n")
    print("*"*8 + "fun2" + "*"*8)
    print("打印当前信息的测试函数,并返回一个整形的" + str(num))
    print("*" * 20)
    print("\n")
    time.sleep(0.25)
    return num

def cs3(num):
    print("cs3函数得到了cs2函数的输出:" + str(num))


cs3(cs2(5))

print("传出的函数的名字: " + str(cs2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper


输出

开始计时,打下时间戳


********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************


传入的函数的id: 2966589466664
完成计时,消耗时间0.26083870000000003


cs3函数得到了cs2函数的输出:5
传出的函数的名字: cs2

进程已结束,退出代码 0

三.装饰器其他使用应用记录

下班了后面来更新,使用。。。。

1.自动重启调用

import time
import functools


def retry_and_show(exception: Exception, tries: int = 3, delay: int = 1, backoff: int = 2):
    """

    :param exception: 错误;类型
    :param tries: 重试次数
    :param delay: 间隔重试时间间隔 秒
    :param backoff: 回退步长
    :return:
    """
    def decorator_retry(func):
        @functools.wraps(func)
        def run_with_retry_police(*args,**kwargs):
            _tries, _delay = tries,delay
            while _tries > 1:
                try:
                    return func(*args,**kwargs)
                except exception:
                    print("程序错误" + str(_delay) + "秒后重新尝试")
                    time.sleep(_delay)
                    _tries -= 1
                    _delay *= backoff
            return func(* args, ** kwargs)

        return run_with_retry_police

    return decorator_retry

视频27.58分说明

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63255.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

再见 Spring Boot 1.X ,Spring Boot 2.X 走向舞台中心

2019年8月6日,Spring 官方在其博客宣布,Spring Boot 1.x 停止维护,Spring Boot 1.x 生命周期正式结束。 其实早在2018年7月30号,Spring 官方就已经在博客进行过预告,Spring Boot 1.X 将维护到2019年8月1日。 1.5.x 将会是 Spring Boot 1.0 系列的最后一个大版本。 如今…

[附源码]计算机毕业设计停车场管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

uniapp——uniapp基础知识

文章目录前言一、HBuilderX常用的快捷键&#xff1f;二、项目中的目录二、项目中的目录总结前言 提示&#xff1a;从0开始学习uniapp&#xff1a; uniapp 解决了什么问题&#xff1f; 1解决了跨端的问题&#xff0c;一端多用。并且可以通过条件编译来实现&#xff0c;不同平台…

【allegro 17.4软件操作保姆级教程九】布线后检查与调整

目录 1.1 PCB状态查看 1.2 添加背钻 1.3 隐藏铜皮检查走线 1.5 位号丝印调整 1.6 使用cut功能删除不需要的电气走线或非电气走线 1.7 走线参考层快速检查 1.8 添加MARK点 1.9 添加工艺边 1.10 尺寸标注 1.1 PCB状态查看 点击display-status可以查看状态&#xff1a; 各…

Spark VS Flink,大数据该学什么

Spark VS Flink&#xff1a;功能比较 Spark和Flink中的功能集在很多方面都不同&#xff0c;如下表所示&#xff1a; Flink特点 支持高吞吐、低延迟、高性能的流处理 有状态计算的Exactly-once语义&#xff0c;对于一条message,receiver确保只收到一次 支持带有事件时间&…

超声功率放大器在MEMS超声测试中的应用

MEMS&#xff08;微机电系统&#xff09;技术的不断发展&#xff0c;目前已经广泛应用在生物、航空、医学、航天等多领域。MEMS传感器即微机电系统&#xff08;Micro-electroMechanicalSystems&#xff09;&#xff0c;是指精密机械系统和微电子电路技术结合发展出来的一项工程…

一文800字手把手教你使用Docker实现分布式Web自动化

01、前言 顺着docker的发展&#xff0c;很多测试的同学也已经在测试工作上使用docker作为环境基础去进行一些自动化测试&#xff0c;这篇文章主要讲述在docker中使用浏览器进行自动化测试如果可以实现可视化&#xff0c;同时可以对浏览器进行相关的操作。 02、开篇 首先我们先…

在RVIZ中进行可视化操作

文章目录可视化图片catkin新建工程主函数代码cmakelists.txt编译和运行在rviz中显示可视化realsense安装realsense SDK在ROS中安装realsense在rviz中显示参考可视化图片 catkin新建工程 mkdir -p rosopencv/srccd rosopencv/srccatkin_create_pkg rosopencv sensor_msgs cv_br…

Python中正则表达式的使用

正则表达式就是从字符串中发现规律&#xff0c;并通过“抽象”的符号表达出来。打个比方&#xff0c;对于2,5,10,17,26,37这样的数字序列&#xff0c;如何计算第7个值&#xff0c;肯定要先找该序列的规律&#xff0c;然后用n21这个表达式来描述其规律&#xff0c;进而得到第7个…

前端骨架屏应用

什么是骨架屏 骨架屏可以理解为在页面数据尚未返回或页面未完成完全渲染前&#xff0c;先给用户呈现一个由灰白块组成的当前页面大致结构&#xff0c;让用户产生页面正在逐渐渲染的感受&#xff0c;从而使加载过程从视觉上变得流畅。 生成后的骨架屏页面如下图所示&#xff1…

第二证券|沪指冲上3200点,中字头太火爆!

轿车整车概念股拉升&#xff0c;多要素共同刺激下&#xff0c;新能源车或将呈现一轮涨价潮。 今天早盘&#xff0c;三大股指走势分解&#xff0c;沪指拉升涨1.56%&#xff0c;站上3200点关口&#xff1b;创业板指则围绕平盘线展开震动&#xff0c;跌0.39%。 以稳妥股为首的大金…

将数据预处理嵌入AI模型的常见技巧

作者&#xff1a;战鹏州 英特尔物联网行业创新大使 目录 1.1 用模型优化器实现数据预处理 1.1.1 模型优化器预处理参数 1.1.2 将ResNet模型的预处理嵌入模型 1.2 用OpenVINO™ 预处理API实现数据预处理 1.3 使用模型缓存技术进一步缩短首次推理时延 1.4 总结 本文将介绍…

机器学习——评估和改进学习算法

0. 引言 当我们运用训练好了的模型来预测未知数据的时候发现有较大的误差&#xff0c;我们下一步可以做什么&#xff1f; 获得更多的训练样本——通常是有效的&#xff0c;但代价较大&#xff0c;下面的方法也可能有效&#xff0c;可考虑先采用下面的几种方法。尝试减少特征的…

2022年最新Android面试题整理,全网都在看,史上最全面试攻略

Android面试现在什么东西是必须考察的&#xff1f;专业素养还是面试技巧&#xff1f;四大组件还是Framework层源码&#xff1f;哪有什么必考的&#xff0c;可以说所有技术栈都不是必考&#xff0c;但又是必考。话虽看似矛盾&#xff0c;但却反映了当前国内互联网环境的现状&…

基于随机油漆优化器 (MOSPO)求解多目标优化问题附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

HTML刷题

HTML5 之前的版本是 HTML4.01 onblur 和 onfocus 属于焦点事件&#xff1a; onblur&#xff1a;失去焦点 onfocus&#xff1a;获取焦点 链接&#xff1a;为同一个文件添加多种文件格式的原因是什么 &#xff1f;&l__牛客网 来源&#xff1a;牛客网 1.为同一个文件添…

vue提取字符串中中文汉字的大写首字母

vue提取字符串中中文汉字的大写首字母 1.需求背景 最近开发vue项目&#xff0c;在关于药品的项目中有个需求是要提取药品中文名称的首字母组成拼音码。 解决方案 这里需要用到pinyin-pro插件&#xff0c;参数介绍如下&#xff1a; 使用如下&#xff1a; // 1、安装piny…

OpenAI ChatGPT注册试用全攻略

OpenAI ChatGPT注册试用全攻略 最近ChatGPT很火&#xff0c;但是有人注册会经常出现不服务当前地区问题&#xff0c;现在手把手教你解决。 如何在中国试用ChatGPT&#xff1f; 这是ChatGPT给的回答 您好&#xff0c;ChatGPT是一种计算机程序&#xff0c;可以自动生成自然语…

C#大型外卖订单调度系统源码PC版 手机版 商户版

开发环境&#xff1a; VS2012 SQL2008R2 菜单功能 1、手机端功能模块 1&#xff09;自动定位或者搜索地址&#xff1a;使用百度地图&#xff0c;实现自动定位&#xff0c;打开微信自动定位&#xff0c;搜索附件外卖商家 2&#xff09;附近外卖商家展示&…

《痞子衡嵌入式半月刊》 第 31 期

痞子衡嵌入式半月刊&#xff1a; 第 31 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻&#xff0c;农历年分二十四节气&#xff0c;希望在每个交节之日准时发布一期。 本期刊是开源项目&#xff08;GitHub: JayHeng/pzh-mcu-bi-weekly&#xff09;&#xff0c;欢…