软件测试/测试开发丨Python闭包与装饰器 学习笔记

点此获取更多相关资料

本文为霍格沃兹测试开发学社学员学习笔记分享
原文链接:https://ceshiren.com/t/topic/27720

闭包与装饰器

1、函数引用

  • Python 中定义的函数,也可以像变量一样,将一个函数名,赋值给另一个变量名,赋值后,此变量名就可以做为该函数的一个别名使用,进行调用函数,此功能列表操作的sort() 方法时使用过,sort()方法的 key 参数传入的就是一个函数名。
def show():print("Show Run ...")show()
a = show
a()
  • 注意:在将一个函数名(函数引用)赋值给一个变量时,函数名后不能添加括号。

2、闭包

(1)闭包定义

闭包(Closure)是指在一个嵌套的函数内部访问其外部函数中定义的变量或函数的能力。换句话说,闭包是一个函数对象,它可以记住并访问它创建时的上下文环境中的变量。

(2)闭包的基本组成部分

  • 定义外部函数和内部函数:内部函数是在外部函数中定义的函数;
  • 内部函数实现对外部函数中变量的操作:可以访问外部函数中的变量和参数,以及外部函数所在的作用域中的变量或其他函数对象,它被内部函数引用并记住,即使外部函数执行完成后仍然存在。
  • 外部函数返回内部函数名供其他地方使用;
def out_func():out_n = 100def inner_func():print(out_n)return inner_funcif __name__ == '__main__':of1 = out_func()of2 = out_func()of1()of2()

(3)闭包的特点

  1. 内部函数可以访问外部函数中定义的变量和参数,即使外部函数已经执行完毕。
  2. 闭包(内函数)可以在外部函数的作用域之外被调用和执行–因为外部函数return了内部函数名出去。
  3. 闭包(内函数)可以访问并修改外部函数中的局部变量,使其具有持久性。

(4)闭包的应用场景

  1. 保护私有变量:可以使用闭包来创建私有变量和方法,通过内部函数的作用域和环境变量,可以实现对外部访问的限制。
  2. 延迟执行:可以使用闭包来延迟某个函数的执行,即在函数外部创建一个闭包,将需要执行的函数作为内部函数,通过调用闭包来触发函数的执行。
  3. 缓存数据:可以使用闭包来缓存一些昂贵的计算结果,以避免重复计算,提高程序的性能。

注意

  • 需要注意的是,在使用闭包时,要注意管理内存,避免产生不必要的内存泄漏问题。

(5) nonlocal关键字

  • 和全局变量一样,在函数内是不能直接修改函数外的变量的,如果修改全局变量需要使用 global 在函数内部声明变量为全局变量。 闭包中要修改变量也是一样,内函数是不能直接修改外函数中定义的变量的,如果需要修改,要在内函数中使用 nonlocal 关键字声明该变量为外函数的变量

A、 不使用 nonlocal 修饰

def out_func():out_n = 100def inner_func():out_n = 200print("inner:",out_n)print("outer1:",out_n)inner_func()print("outer2:",out_n)return inner_funcif __name__ == '__main__':of1 = out_func()of1()
# 结果:
# outer1: 100
# inner: 200
# outer2: 100
# inner: 200

B、 使用 nonlocal 修饰

def out_func():out_n = 100def inner_func():nonlocal out_nout_n = 200print("inner:",out_n)print("outer1:",out_n)inner_func()print("outer2:",out_n)return inner_funcif __name__ == '__main__':of1 = out_func()of1()
# 结果:
# outer1: 100
# inner: 200
# outer2: 200
# inner: 200

3、装饰器

(1)装饰器定义

  • 装饰器是Python提供的一种语法糖,装饰器使用@ 符号加上装饰器名称,用于修改其他函数的行为,并且在不修改原始函数定义和调用的情况下添加额外的功能
  • 装饰器提供了一种简洁而优雅的方式来扩展和修改函数或类的功能。它本质上就是一个闭包函数

(2)装饰器的功能特点

  • 不修改已有函数的源代码
  • 不修改已有函数的调用方式
  • 给已有函数增加额外的功能

(3)装饰器的使用和级别组成

  1. 定义一个外部函数outer,并将原函数名func作为参数;
  2. 定义一个内部函数inner,在内部函数中调用func函数,以及添加新增功能;
  3. 外部函数返回内部函数名inner;
  4. 将外部函数名使用@装饰到原函数,即@outer;
# 需求,在不改动原函数基础上给原函数增加统计运行时间功能
import time
def run_time(func):def inner():# 开始计时start_time = time.time()# 原函数执行func()# 结束计时end_time = time.time()print(f"程序运行时间为:{end_time - start_time}秒")return inner
@run_time
# 原函数
def show():for i in range(1,13):print(f"第{i}次打印")if __name__ == '__main__':show()

(4)装饰器的本质

  • 装饰器提供了一种简洁而优雅的方式(语法糖)来扩展和修改函数或类的功能。其本质就是函数的使用。

  • 语法糖: 在计算机科学中,语法糖(Syntactic sugar)是指一种语法上的扩展,它并不改变编程语言的功能,只是提供了更便捷、更易读的写法,使得代码更加简洁和可理解。

    • 常见的语法糖:

      • 推导式
      • 装饰器
      • 切片
      • 上下文管理器

(5)装饰器执行的内部过程

  1. Python解释器在遇到装饰器时,会将被装饰函数引用做为参数传递给闭包的外函数,外函数执行后,返回内函数的引用,此时,再将内函数引用赋值给被装饰器函数。
  2. 当Python解释器执行完装饰过程后,被装饰函数的函数名就不在保存原函数的引用,而是保存的闭包函数inner的引用。
  3. 当执行被装饰函数时,实际执行的是闭包函数inner,由inner间接调用被装饰函数,完成整个调用过程。
@run_time
# 原函数
def show():

Python解释器解释过程:

show = run_time(show)

前面示例代码可修改为:

import time
def run_time(func):def inner():# 开始计时start_time = time.time()# 原函数执行func()# 结束计时end_time = time.time()print(f"程序运行时间为:{end_time - start_time}秒")return inner# 原函数
def show():for i in range(1,20):print(f"第{i}次打印")if __name__ == '__main__':show = run_time(show)# run_time返回innershow()# 这里执行的就是inner()函数

(6) 通用装饰器

  • 如果需要装饰器可以装饰任何函数,那么就需要解决被装饰函数的参数及返回值的问题。
  • 可以通过可变参数和在内函数中返回被装饰函数执行结果的形式解决此问题。
# 做为装饰器名的外函数,使用参数接收被装饰函数的引用
def decorator(func):# 内函数的可变参数用来接收被装饰函数使用的参数def inner(*args, **kwargs):# 装饰器新增功能代码# 调用被装饰函数,并将接收的参数传递给被装饰函数,保存被装饰函数执行结果result = func(*args, **kwargs)# 返回被装饰函数执行结果return result# 返回内函数引用return inner

(7)带参数装饰器

  • 除了普通的装饰器使用方式外,在使用装饰器时,还需要向装饰器传递一些参数,比如测试框架 pytest 实现数据驱动时,可以将测试数据以装饰器参数形式传入,此时,前面定义的做为装饰器的闭包形式就不能满足需求了。
  • 可以在通用装饰器外,再定义一层函数,用来接收装饰器的参数。
# 接收装饰器参数的函数
# 参数一:以字符串形式接收被装饰函数的参数列表,需要与被装饰函数参数名保持一致,例:"a,b,c"
# 参数二:以[(),(),()] 形式传入驱动数据。
def decorator_args(vars, datas):def decorator(func):# 将字符串参数分割备用v_keys = vars.split(",")# 定义保存 [{},{},{}] 形式的数据new_datas = []# 遍历数据,取出一组元素数据for item in datas:# 定义一个新字典,用来保存 变量名与传入数据组成的字典d_item = {}# 使用 zip 函数,同时遍历两个元组,变量名做为key, 元素数据做为valuefor k, v in zip(v_keys, item):# 将 变量名和值对应保存到字典中d_item[k] = v# 将组合好的字典追加到新数据中备用new_datas.append(d_item)def inner(*args, **kwargs):return func(*args, **kwargs)# 遍历新数据,取出元素字典for item in new_datas:# 将字典中的数据解包传给内函数inner(**item)return innerreturn decorator# 数据驱动数据
data = [(1,2,3),(4,5,6),(7,8,9)]# 装饰器传参
@decorator_args("a,b,c", data)
def show(a,b,c):print(a,b,c)
装饰器传参原理

装饰器传参的本质就是链式语法的多次函数调用;

@decorator_args("a,b,c", data) 解析:

  1. 先执行 decorator_args("a,b,c", data)部分
  2. 得到结果 decorator@结合变成装饰器形式@decorator
  3. 通过结果 @decorator 装饰器正常装饰被装饰函数

4、作业

  • 作业要求:

    • 编写一个Python程序,实现一个计数器函数,该函数能够记录特定函数的调用次数。你需要使用闭包和装饰器来实现这个功能。

(1)使用闭包方式实现

"""
作业要求
编写一个Python程序,实现一个计数器函数,该函数能够记录特定函数的调用次数。你需要使用闭包和装饰器来实现这个功能。
"""
#给原函数增加统计调用次数功能
def count(func):n = 0def inner(number_1,operate,number_2):nonlocal nfunc(number_1,operate,number_2)n += 1print(f"函数被调用了{n}次")return inner# 原函数:简单打印操作
def compute(number_1,operate,number_2):match operate:case "+":print(number_1 + number_2)case "-":print(number_1 - number_2)case "*":print(number_1 * number_2)case "/":print(number_1 / number_2)case _:print("对不起,您要求的计算我不会!")if __name__ == '__main__':nwe_computer = count(compute)nwe_computer(5,"+",1)nwe_computer(5,"-",1)nwe_computer(5,"*",1)nwe_computer(5,"/",1)nwe_computer(5,"//",1)

(2)使用装饰器方式实现

"""
作业要求
编写一个Python程序,实现一个计数器函数,该函数能够记录特定函数的调用次数。你需要使用闭包和装饰器来实现这个功能。
"""
#给原函数增加统计调用次数功能
def count(func):n = 0def inner(number_1,operate,number_2):nonlocal nfunc(number_1,operate,number_2)n += 1print(f"函数被调用了{n}次")return inner# 原函数:简单打印操作
@ count
def compute(number_1,operate,number_2):match operate:case "+":print(number_1 + number_2)case "-":print(number_1 - number_2)case "*":print(number_1 * number_2)case "/":print(number_1 / number_2)case _:print("对不起,您要求的计算我不会!")if __name__ == '__main__':compute(5,"+",1)compute(5,"-",1)compute(5,"*",1)compute(5,"/",1)compute(5,"//",1)

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

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

相关文章

openlayers 实例教程分享

OpenLayers3示例 记录一个openlayers 实例教程网址 1、WebGIS简介 语雀 记录一个openlayers入门教程 地图坐标系转换 - Online Tools 记录一个国家2000坐标转换工具的方法 记录一次我使用openlayers 开发地图的实例,实现了打点,弹窗&#xff…

linux 系统时间、时区、date、timedatectl

一、名词 UTC 格林尼治标准时间,位于0时区,东八区需要8 RTC 硬件时间,也称作BIOS时间 CST 中央标准时间,也指各个时区自己的地方时间二、查看时间 #查看当前硬件时间 hwclock #查看当前系统时间 date date -u # 显示UTC时间 d…

Docker 的数据管理和Dockerfile镜像的创建

目录 Docker 的数据管理 管理 Docker 容器中数据的方式 端口映射 容器互联(使用centos镜像) Docker 镜像的创建 Dockerfile 操作常用的指令 编写 Dockerfile 时格式 Dockerfile 案例 Docker 的数据管理 管理 Docker 容器中数据的方式 管理 Doc…

【ES】--track_total_hits参数影响ES分页数据

目录 一、前言二、解决方法2.1、修改max_result_window参数2.2、修改track_total_hits 参数2.3、结论 一、前言 工作遇到一个ES深度分页查询时出现报错,报错内容如下 ElasticsearchException[Elasticsearch exception [typeillegal_argument_exception, reasonResu…

C# AnimeGAN 漫画风格迁移 动漫风格迁移 图像卡通化 图像动漫化

效果 项目 模型 animeganv3_H40_model.onnx animeganv3_H50_model.onnx animeganv3_H64_model.onnx AnimeGANv3_JP_face_v1.0.onnx AnimeGANv3_PortraitSketch_25.onnx Hayao-60.onnx Hayao_64.onnx Paprika_54.onnx Shinkai_53.onnx 下载 可执行文件exe下载 源码下载

JAVA IO 流分类整理

一、JAVA IO 流分为三种: 1、按照流向:输入流和输出流 2、按照操作单元:字节流和字符流 3、按照流的角色:节点流和处理流 二、JAVA IO 流的40多个类都派生自4个抽象类 1、字节输入流(InputStream) 、 2、字…

Pycharm 2023 设置远程调试

pycharm 版本 : 2023.2.1 整体流程参考:https://blog.csdn.net/xuanhaolaile/article/details/128293254 首先确定远程服务器上已经安装好 requirements.txt 中所需的依赖包。 1、SSH Configurations 添加远程服务器 2、Python Interpreter 注意&…

如何让CI/CD同一个阶段的任务先后执行而不是同时执行

可以通过指定同一阶段内的作业之间的依赖关系来定义它们的执行顺序。这确保了一个作业必须在另一个作业开始之前成功完成。具体的方法可能因使用的CI/CD系统而有所不同: GitLab CI/CD: 在GitLab CI/CD中,可以使用needs关键字来指定同一阶段内…

spring boot自定义配置时在yml文件输入有提示

自定义一个配置类&#xff0c;然后在yml文件具体配置值时&#xff0c;一般不会有提示&#xff0c;这个解决这个问题 依赖 <!--自定义配置类&#xff0c;在yml文件写的时候会有提示--><dependency><groupId>org.springframework.boot</groupId><arti…

Godot2D角色导航-自动寻路教程(Godot实现角色随鼠标移动)

文章目录 运行结果2D导航概述开始前的准备2D导航创建导航网格创建角色 其他文章 运行结果 2D导航概述 Godot为2D和3D游戏提供了多个对象、类和服务器&#xff0c;以便于基于网格或基于网格的导航和路径查找。 说到导航&#xff0c;就得说一下导航网格&#xff0c;导航网格定义…

视频缩放的概念整理-步长数组

最近在读ffmpeg的代码时候&#xff0c;这个接口不是很能看懂int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[]); 多方请教后&#xff0c;记录结果如…

json转对象

要将 JSON 转换为对象&#xff0c;你可以使用不同编程语言中的相应方法或库。以下是一些示例&#xff1a; JavaScript: 在 JavaScript 中&#xff0c;你可以使用 JSON.parse() 方法将 JSON 字符串转换为对象。例如&#xff1a; var jsonString {"name": "John&…

多标签分类论文笔记 | ML-Decoder: Scalable and Versatile Classification Head

个人论文精读笔记&#xff0c;主要是翻译心得&#xff0c;欢迎旁观&#xff0c;如果有兴趣可以在评论区留言&#xff0c;我们一起探讨。 Paper: https://arxiv.org/pdf/2111.12933.pdf Code: https://github.com/Alibaba-MIIL/ML_Decoder 文章目录 0. 摘要1. 介绍2. 方法2.1 Ba…

免疫球蛋白介绍

免疫球蛋白&#xff08;Immunoglobulin&#xff0c;Ig&#xff09;是广泛存在于哺乳动物血清、淋巴液、组织液和外分泌液中的一种具有抗体活性或化学结构与抗体相似的球蛋白&#xff0c;在机体防御疾病的重要成分在疾病研究、药物研发、疫苗评价中具有重要作用。抗体&#xff0…

【实训项目】“优品果园”-线上水果商城小程序

1.项目背景 随着现代人对消费水平的追求以及对食物安全的需要&#xff0c;无污染、产地直销的有机水果受到越来越多市民的喜欢。交易过程的简洁化是现代消费者的追求&#xff0c;产地直销也是近期流行的一种新型的交易模式。产地直销的交易模式使得交易过程更加简便快捷&#…

【MySQL】关于MySQL升级到8.0版本的实践方案

关于MySQL升级到8.0版本的实践方案 关于数据库版本升级,一直都是热议话题,对于升级的缘由各家也有所不同,有业务驱动的,有DBA自发驱动的,有规划导向也有方向指引的……抛开各种原因,当升级这个决定落下来的时候,对于DBA手头的几百几千套数据库来说,就好比是一场动物大…

[电源选项]没有系统散热方式,没有被动散热选项

背景 笔记本的风扇声音太大&#xff0c;想改成被动散热方式&#xff0c;又不想影响性能。 于是我打开了控制面板\所有控制面板项\电源选项&#xff0c;点更改计划设置-> 更改高级电源设置。 想把散热方式改成被动散热。发现win11中好像没有这个选项了&#xff01; 如何…

198、RabbitMQ 的核心概念 及 工作机制概述; Exchange 类型 及 该类型对应的路由规则;了解什么是JMS。

目录 JMS 讲解★ RabbitMQ的核心概念★ RabbitMQ工作机制★ Connection&#xff08;连接&#xff09; 与 Channel&#xff08;通信信道&#xff09;★ Exchange★ Exchange与Queue★ Exchange的类型&#xff08;4种&#xff09;及 该类型对应的路由规则 看RabbitMQ 之前&#x…

技术分享| 二进制部署MySQL

一、介绍 ​MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS (Relational Database Management System&#x…

拼多多历史价格数据接口,拼多多商品历史价格接口,拼多多API接口

采集拼多多商品历史价格接口可以采用以下方式&#xff1a; 使用价格监控工具。价格监控工具是一种可以自动监测商品价格变化的工具&#xff0c;它可以帮助消费者快速采集拼多多商品价格信息&#xff0c;还可以提供价格变动趋势的图表分析&#xff0c;使消费者更好地掌握商品价…