Python - Decorator(装饰器) - 带参数的

2023-05-16

我们通过以示例来看看带参数的装饰器到底怎么回事。

from time import perf_counter
from functools import wraps

def repeated(times):

    def outer(fn):
        @wraps(fn)
        def inner(*args, **kwargs):
            for i in range(0, times):
                result = fn(*args, **kwargs)
            return result

        return inner

    return outer

def timed(fn):

    @wraps(fn)
    def inner(*args, **kwargs):
        start = perf_counter()
        result = fn(*args, **kwargs)
        elapsed = perf_counter() - start
        print(f'{fn.__name__} took {elapsed:.6f}s to execute')
        return result

    return inner

def print_split_line():
    print('*' * 50)

print_split_line()

@repeated(5)
@timed
def func1():
    print('func1 running')   
func1()

print_split_line()

@timed
@repeated(5)
def func2():
    print('func2 running')
func2()

print_split_line()

def func3():
    print('func3 running')
func3 = repeated(5)(timed(func3))
func3() 

print_split_line()

def func4():
    print('func4 running')
func4 = timed(repeated(5)(func4))
func4()

print_split_line()

输出如下:


**************************************************
func1 running
func1 took 0.000034s to execute
func1 running
func1 took 0.000020s to execute
func1 running
func1 took 0.000020s to execute
func1 running
func1 took 0.000020s to execute
func1 running
func1 took 0.000019s to execute
**************************************************
func2 running
func2 running
func2 running
func2 running
func2 running
func2 took 0.000109s to execute
**************************************************
func3 running
func3 took 0.000031s to execute
func3 running
func3 took 0.000023s to execute
func3 running
func3 took 0.000020s to execute
func3 running
func3 took 0.000035s to execute
func3 running
func3 took 0.000022s to execute
**************************************************
func4 running
func4 running
func4 running
func4 running
func4 running
func4 took 0.000120s to execute
**************************************************  

说明:

  • repeated decorator就是多次重复执行被装饰的函数,并把被装饰函数的最后一次执行结果返回
  • timed decorator就是打印被装饰函数的执行时间
  • @语法糖等价关系
@repeated(5)
@timed
def func1():
    print('func1 running') 
def func1():
    print('func1 running')

func1 = repeated(5)(timed(func1))

@repeated(5)中的repeated(5)是不是很眼熟?不错,repeated(5)就是一次函数调用!然后repeated(5)的返回值是一个真正的装饰器,我们就可以用在@语法糖上了。我们也可以说repeated()是个装饰器工厂函数!

  • 说明有时候多个装饰器的情况下,加入装饰器的顺序会影响执行结果。

思考1:

为什么以下代码不能实现带参数的装饰器?

from functools import wraps

def repeated(fn, times):
    @wraps(fn)
    def inner(*args, **kwargs):
        for i in range(0, times):
            result = fn(*args, **kwargs)
        return result
    return inner

@repeated(5)
def func():
    print('func running')

func()

运行会得到以下异常:


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-60-a0fe82c182b7> in <module>
      9     return inner
     10 
---> 11 @repeated(5)
     12 def func1():
     13     print('func1 running')

TypeError: repeated() missing 1 required positional argument: 'times'  

这个比较明显,repeated(5)就是函数调用,但repeated需要2个位置参数,而repeated(5)只提供了一个位置参数。显然,此处的函数调用出错!

思考2:

为什么以下代码不能实现带参数的装饰器?

from functools import wraps

def repeated(fn, times):
    @wraps(fn)
    def inner(*args, **kwargs):
        for i in range(0, times):
            result = fn(*args, **kwargs)
        return result
    return inner

@repeated(func, 5)
def func():
    print('func running')   

func()

运行会得到以下异常:


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-4-febc2a5c7b04> in <module>
      9     return inner
     10 
---> 11 @repeated(func, 5)
     12 def func():
     13     print('func running')

NameError: name 'func' is not defined  

怎么是这个异常?为什么正确实现的带参数或者不带参数的装饰器能找到下面的函数名而没有这个异常?

我想这个和Python解释器有关系, 当到达@repeated(func, 5)这行代码时,Python应该在执行repeated(func, 5)调用,然而此时func函数还没定义,func标签也不存在,所以报当前错误。

思考3:

思考2中出现的错误,有没有可能不用@语法糖的时候,是可以实现的?直接上代码。

from functools import wraps

def repeated(fn, times):
    @wraps(fn)
    def inner(*args, **kwargs):
        for i in range(0, times):
            result = fn(*args, **kwargs)
        return result
    return inner

def func():
    print('func running')
    
func = repeated(func, 5)(func)     # 此处代码近似于 @repeated(func, 5) 语法糖

func()

运行出现如下异常:


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-3398bd4a9573> in <module>
     12     print('func running')
     13 
---> 14 func = repeated(func, 5)(func)
     15 
     16 func()

<ipython-input-3-3398bd4a9573> in inner(*args, **kwargs)
      5     def inner(*args, **kwargs):
      6         for i in range(0, times):
----> 7             result = fn(*args, **kwargs)
      8         return result
      9     return inner

TypeError: func() takes 0 positional arguments but 1 was given  

说明:

repeated(func, 5)执行没有问题,能正确返回repeated.inner函数。

然后repeated.inner就会继续执行,请注意此时传给repeated.inner函数有一个位置参数func,在repeated.inner内部,func会被调用,并且func本身作为一个位置参数传给func,但根据func定义,func是不解释任何参数的,所以此时报错!

其实可以继续做一些事情,让本思考中的代码运行起来,但那已经没有意义!

总结:

  • @语法糖本省就给我们使用格式的限制
  • 带参数的装饰器,我们也可以认为是装饰器工厂函数,只有调用一次才返回真正意思上的装饰器
  • 在带参的@语法糖出,其实会执行2次函数调用,而在不带参的@语法糖处,只执行一次函数调用
  • 不带参的装饰器只需要嵌套一层子函数,而带参的装饰器需要嵌套二层子函数!
  • 装饰器从某种意义上讲,就是实现了传参。

最内层:args, kwargs,留给目标函数调用时使用的

从内向外第2次:fn,这层就是留给装饰那个目标函数的

从内向外第3层:extra args, 这层才是给装饰器本身的参数

其实大家只要记住带参和不带参的装饰器的标准实现方式就足够应付大多数应用场景了!

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

Python - Decorator(装饰器) - 带参数的 的相关文章

  • ESP8266下载

    下载引脚介绍 VCC span class token operator lt 61 span span class token operator 61 61 span span class token operator 61 61 spa
  • libssl-dev : 依赖: libssl1.0.0 (= 1.0.2g-1ubuntu4.13) 但是 1.0.2n-1ubuntu5.1 正要被安装

    ubuntu下apt get install安装软件 xff0c 报 无法修正错误 xff0c 因为您要求某些软件包保持现状 xff0c 就是它们破坏了软件包间的依赖关系 比如以下报错 一般出现这种情况的原因时 要装A xff0c 依赖B
  • 高通平台音频调试常见问题点归纳

    以下是关于高通音频调试中遇到的一些常见问题点归纳 xff0c 仅供参考 xff0c 如有错误 xff0c 请指正 xff01 1 Audio EC VOIP 软件主要需要设置EC REF echo reference 信号 xff0c 在A
  • 蚂蚁金服二轮面试(P7岗)经验分享

    特意注册了个新号 xff0c 发表下记录自己此次蚂蚁金服的面试情况 xff0c 为了感谢大家面试经历的分享 xff0c 也是对自己面试的总结和复盘 上周三面试 xff0c 截止到现在一周过去啦 xff0c 还没有消息 xff0c 面试过程也
  • 堪比当年的LSTM,Transformer引燃机器学习圈:它是万能的

    视学算法报道 转载自公众号 xff1a 机器之心 作者 xff1a 魔王 谷歌研究科学家 David Ha xff1a Transformer 是新的 LSTM 2017 年 6 月谷歌发布论文 Attention is All You N
  • linux 睡眠函数——sleep(),usleep()

    http blog csdn net gpengtao article details 7887293 include lt unistd h gt unsigned int sleep unsigned int seconds 睡眠秒 返
  • 软件工程复试——九、面向对象方法学引论

    九 面向对象方法学引论 面向对象方法学的出发点和原则是尽可能模拟人类思维方式 xff0c 使开发软件的方法与过程尽可能接近人类认识世界解决问题的方法与过程 xff0c 使描述空间的问题域与求解域在结构上保持一致 面向对象方法的四个要点 xf
  • FreeRTOS+TCP模块移植

    上一版本移植并没有写的很详细 xff0c 只是将改好的代码贴上去 xff0c 今天更新一版 xff0c 附带资源 上一版本用的是FreeRTOS V10 0 1 这一版采用了最新的FreeRTOS V10 3 1 在正确移植FreeRTOS
  • PID控制器讲解

    这个视频教程讲的非常好 xff0c 从理论层面到应用 xff0c 强烈推荐有兴趣的同学看一下 https www bilibili com video BV1B54y1V7hp
  • Python学习笔记丨while、for、if循环结构基础知识与易错点

    Python流程控制 本篇笔记的主要内容是 xff1a 条件控制和循环控制 xff0c 包括if语句 while语句 for语句等 Python条件控制 span class hljs keyword style color c678dd
  • R语言安装R包的方法,mac、windows、linux安装R包常见问题与解决方法

    R语言如何快速安装R包 xff1f 如果把R比作是沃土的话 xff0c 那么R包就是鲜花 xff0c 开源共享的开发者社区提供了很多功能丰富的R包 xff0c 方便使用者充分利用R语言完成工作 但是 xff0c 有时候在安装R包是会遇到各种
  • kube-ovn代码系列(四)pod 安全组功能

    kube ovn代码系列 xff08 四 xff09 pod 安全组功能 链接 https www gogo dev com index php 2022 02 19 kube ovn securitygroup 内容 kube ovn在1
  • Ubuntu20.04下运行VINS系列:VINS-Mono、VINS-Fusion和GVINS

    文章目录 一 安装VINS Mono1 1 适配Ceres2 1 01 2 适配OpenCV41 3 编译运行 二 安装VINS Fusion2 1 适配Ceres2 1 0和OpenCV42 2 编译运行2 2 1 EuRoC数据集2 2
  • 最小花费

    题目描述 在n个人中 xff0c 某些人的银行账号之间可以互相转账 这些人之间转账的手续费各不相同 给定这些人之间转账时需要从转账金额里扣除百分之几的手续费 xff0c 请问A最少需要多少钱使得转账后B收到100元 输入格式 第一行输入两个
  • 传感器融合sensor fusion

    自动控制系统中的传感器融合 传感器融合的4个作用 xff1a 1 增加数据质量 比如减少噪声 xff1b 2 增加可靠性 多传感器互为备份 xff1b 3 估计预测状态 xff1b 4 可增加被测范围 相对于单个传感器来说 xff0c 多传
  • 摄像机成像原理(模型)与标定

    一般摄像机简化为小孔成像的理想模型 xff08 线性模型 xff09 xff0c 因为摄像机镜头 xff08 视场角 xff09 很小 xff0c 相当于被拍摄物体通过小孔投影到感光元件CCD CMOS上 对于加了各种镜头的摄像机 xff0
  • 实习周记2

    在组长准备给我布置小任务的时候 xff0c 公司开了一个新的项目并且缺前端 xff0c 我就被分配到新项目中去 xff0c 这个项目使用 angular 43 bootstrap前端框架 这不是一个初次开发的项目 xff0c 而是一个需要修
  • OBJ可视化——UV还原(修正)

    前言 前面写过一篇obj格式解析的博客 xff0c 但是这篇文章中可视化的工作是参考PRNet的源码进行的 xff0c 后来细细思考了一下 xff0c 有点问题 xff0c 具体看下面 问题来源 在PRNet源码的render py中有个函
  • Unity中BVH骨骼动画驱动的可视化理论与实现

    前言 找了很久使用BVH到unity中驱动骨骼动画的代码 xff0c 但是都不是特别好用 xff0c 自己以前写过 xff0c 原理很简单 xff0c 这里记录一下 理论 初始姿态 在BVH或者其它骨骼动画中 xff0c 一般涉及到三种姿势
  • 卡通驱动项目ThreeDPoseTracker——模型驱动解析

    前言 之前解析过ThreeDPoseTracker这个项目中的深度学习模型 xff0c 公众号有兄弟私信一些问题 xff0c 我刚好对这个项目实现有兴趣 xff0c 就分析一波源码 xff0c 顺便把问题解答一下 这个源码其实包括很多内容

随机推荐

  • 卡通驱动项目ThreeDPoseTracker——关键点平滑方案解析

    前言 之前对ThreeDPoseTracker的深度学习模型和unity中的驱动方法进行过解析 xff0c 还有一个比较重要的就是从深度学习模型出来的3D关键点数据会有抖动 xff0c 在ThreeDPoseTracker源码中有做两次平滑
  • 卡通角色表情驱动系列一

    前言 分析完ThreeDPoseTracker来做卡通角色的身体驱动 xff0c 接下来在卡通驱动领域还有一个是表情驱动 对这个真的是一窍不通啊 xff0c 只能慢慢看论文了 国际惯例 xff0c 参考博客 论文 xff1a Landmar
  • opencv相机标定和人头姿态估计案例

    前言 头部驱动除了之前关注的表情驱动外 xff0c 还有眼球驱动和头部方向驱动 本博客基于opencv官方文档和部分开源代码来研究如何基于人脸关键点获取头部的朝向 国际惯例 xff0c 参考博客 xff1a opencv Camera Ca
  • 卡通角色表情驱动系列二

    前言 之前介绍了使用传统算法求解BS系数的表情驱动方法 xff0c 其中提到过的三种方法之一是基于网格形变迁移做的 xff0c 那么这篇文章就是对 Deformation Transfer for Triangle Meshes 做表情驱动
  • HDU 1085 Holding Bin-Laden Captive!(母函数)

    HDU 1085 Holding Bin Laden Captive xff08 母函数 xff09 题目地址 题意 xff1a 给你cnt1个一元硬币 xff0c cnt2个两元硬币 xff0c cnt3个五元硬币 xff0c 问不能凑出
  • UE自带重定向原理

    UE自带重定向方法验证 核心源码在VS的解决方案中的位置 xff1a UE4 Source Developer AssetTools Private AssetTypeActions AnimSequence cpp中第3237行Remap
  • matlab之bsxfun函数

    lt span style 61 34 font size 18px color ff0000 34 gt 简单的调用方法 xff1a lt span gt bsxfun 64 plus xff0c A xff0c B xff0c 其中 6
  • 关于协方差矩阵需要注意的一个事项

    协方差矩阵是衡量样本的属性 即维度 之间的关系 xff0c 而不是样本与样本之间的关系 比如有100个样本 xff0c 每个样本10个属性 xff0c 那么计算得到的协方差矩阵一定是10 10的 xff0c 而不是100 100的 xff0
  • 【caffe-Windows】caffe+VS2013+Windows+GPU配置+cifar使用

    前言 国际惯例 xff0c 先来波地址 xff1a CUDA WIN7 xff1a 链接 xff1a http pan baidu com s 1nvyA3Qp 密码 xff1a h0f3 官方网址 xff1a https develope
  • 【caffe-Windows】以mnist为例lmdb格式数据

    前言 前面介绍的案例都是leveldb的格式 xff0c 但是比较流行和实用的格式是lmdb xff0c 原因从此网站摘取 它们都是键 值对 xff08 Key Value Pair xff09 嵌入式数据库管理系统编程库 虽然lmdb的内
  • 【theano-windows】学习笔记十——多层感知机手写数字分类

    前言 上一篇学习了softmax 然后更进一步就是学习一下基本的多层感知机 MLP 了 其实多层感知机同时就是w x 43 b用某个激活函数激活一下 得到的结果作为下一层神经元的输入x 类似于 o u t p u t 61 f 3 f 2
  • 【theano-windows】学习笔记二十——LSTM理论及实现

    前言 上一篇学习了RNN xff0c 也知道了在沿着时间线对上下文权重求梯度的时候 xff0c 可能会导致梯度消失或者梯度爆炸 xff0c 然后我们就得学习一波比较常见的优化方法之LSTM 国际惯例 xff0c 参考网址 xff1a LST
  • 【TensorFlow-windows】keras接口——ImageDataGenerator裁剪

    前言 Keras中有一个图像数据处理器ImageDataGenerator xff0c 能够很方便地进行数据增强 xff0c 并且从文件中批量加载图片 xff0c 避免数据集过大时 xff0c 一下子加载进内存会崩掉 但是从官方文档发现 x
  • 梯度下降法与Logistic Regression 及 Matlab 代码

    梯度下降法与Logistic Regression 及 Matlab 代码 前言Logistic回归梯度下降法例子1 xff0c 固定学习率改进1 xff1a 正则化改进2 xff1a 动态学习率查看分类效果不足完整代码 前言 本质是一个求
  • ONOS 控制器安装和app新建和编译

    1 1 ONOS 控制器编译与安装 ONOS 1 8 版本起强制使用 BUCK 构建工具 xff0c 不再使用 maven xff0c 编译和打包方式与旧版本有所区别 步骤 xff1a 配置环境 gt 下代码 gt 编译 gt 运行 配置环
  • SQLyog(navica)连接docker容器中的mysql8.0.12 报错1251或2003解决办法

    使用SQLyog xff08 navicat xff09 远程连接docker容器中的mysql8 0 12 报以下错误 解决办法 xff1a 一 在docker中启动mysql 定义端口号3306 root 64 localhost do
  • TTY 到底是个什么玩意?

    先来回答一道面试题 xff1a 我们知道在终端中有一些常用的快捷键 xff0c Ctrl 43 E 可以移动到行尾 xff0c Ctrl 43 W 可以删除一个单词 xff0c Ctrl 43 B 可以向前移动一个字母 xff0c 按上键可
  • 如何画好一份架构图

    先说答案 画架构图分四步走 xff1a 第一 xff0c 搞清楚要画的架构图的类型 xff1b 第二 xff0c 确认架构图中的关键要素 xff08 比如产品 技术 服务 xff09 xff1b 第三 xff0c 梳理关键要素之间的关联 x
  • NVIDIA Jetson Xavier NX 深度学习相关组件安装

    一 tensorflow的安装 写在前面的牢骚话 xff08 可选择直接跳过 xff09 在写安装tensorflow的教程之前 xff0c 我一定要放出当时我安装tensorflow时所遇到的那些莫名其妙的错误 xff0c 具体错误如下图
  • Python - Decorator(装饰器) - 带参数的

    我们通过以示例来看看带参数的装饰器到底怎么回事 from time import perf counter from functools import wraps def repeated times def outer fn 64 wra