闭包与装饰器

章节目录:

    • 一、闭包
      • 1.1 相关概述
      • 1.2 基本使用
    • 二、装饰器
      • 2.1 相关概述
      • 2.1 装饰器最早版本
      • 2.3 基本使用
      • 2.4 装饰带有参数的函数
      • 2.5 装饰带有返回值的函数
      • 2.6 通用装饰器
      • 2.7 带有参数的装饰器
      • 2.8 类装饰器
      • 2.9 装饰器的使用场景
    • 三、装饰器总结
    • 四、结束语

一、闭包

Python 中的闭包是一种高级特性,它可以让我们更加灵活地使用函数。

1.1 相关概述

  • 闭包closure )指的是在函数内部定义了另外一个函数,并返回了这个内部函数作为函数对象,同时还保存了外层函数的状态信息。这个内部函数可以依赖外层函数的变量和参数,而且外层函数返回的是这个内部函数的引用。这种在函数内部定义函数并返回的方式称为闭包。
  • 简而言之,闭包就是能够读取外部函数内的变量的函数。
  • 闭包的作用
    • 闭包是将外层函数内的局部变量和外层函数的外部连接起来的一座桥梁
    • 将外层函数的变量持久地保存在内存中。
  • 构成条件
    • 函数嵌套(函数里面在定义函数)的前提下。
    • 内部函数使用了外部函数的变量(还包括外部函数的参数)。
    • 外部函数返回了内部函数。

1.2 基本使用

  • 步骤思路

    • 定义外部函数。
    • 定义外部函数,在内部函数中使用外部函数的变量。
    • 外部函数返回内部函数的地址。
  • 代码示例

# 1.定义外部函数。
def outer(x):# 2.定义内部函数。(内部函数使用到外部函数的变量。)def inner(y):print(f"outer_x={x} ,inner_y={y}")return x + y# 3.外部函数返回内部函数。return innerif __name__ == '__main__':# 创建闭包实例。o = outer(5)# 调用闭包。print(o(3))# outer_x=5 ,inner_y=3# 8
  • 优点
    • 变量长期驻扎在内存中,可以重复使用变量。
    • 避免全局变量的污染。
    • 私有成员的存在。
  • 缺点
    • 由于是常驻内存, 则会增加内存开销 ,使用不当甚至会造成内存的泄露,所以不能滥用闭包
    • 解决方法是在退出函数之前,将不使用的变量全部删除

二、装饰器

装饰器本身就是一个闭包,它可以保留被装饰函数的状态信息,并在被装饰函数执行前后添加额外的功能

2.1 相关概述

  • 装饰器的作用
    • 不修改已有函数的源码
    • 不修改已有函数的调用方式
    • 给函数添加功能

2.1 装饰器最早版本

  • 代码示例
def check(fn):def inner():# 添加一个登录验证的功能。print("请先登录....")# 调用原函数。fn()return innerdef comment():print("发表评论")if __name__ == '__main__':# 使用装饰器来装饰函数。comment = check(comment)comment()# 请先登录....# 发表评论
  • Python 给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰

2.3 基本使用

  • 代码示例(语法糖)
# 定义一个装饰器。
def browser(fn):# 内部函数:实现需要添加的新功能。def open():print("打开浏览器...")# 调用原函数。fn()print("关闭浏览器!")# 返回内部函数。return open@browser
# @browser为语法糖等同于:login = browser(login)
def login():print("登录...")if __name__ == '__main__':login()# 打开浏览器...# 登录...# 关闭浏览器!

2.4 装饰带有参数的函数

  • 代码示例
def logger(fn):"""装饰带有参数的函数。:param fn::return:"""def inner(a, b):print("--- 开始计算 ---")fn(a, b)return inner@logger
def my_sum(a, b):print(a + b)if __name__ == '__main__':my_sum(3, 6)# --- 开始计算 ---# 9

2.5 装饰带有返回值的函数

  • 不管原函数有没有返回值,装饰器的内部函数都应该将原函数的返回值进行返回
  • 如果原函数有返回值,返回的就是原函数的返回值。
  • 如果没有返回的是 None
  • 代码示例
def logger(fn):"""装饰带有返回值的函数。:param fn::return:"""def inner(a, b):print("--- 开始计算 ---")result = fn(a, b)return resultreturn inner@logger
def my_sum(a, b):return a + bif __name__ == '__main__':print(my_sum(3, 6))# --- 开始计算 ---# 9

2.6 通用装饰器

  • 代码示例
def decorate(fn):"""接受任意参数的装饰器。:param fn::return:"""def inner(*args, **kwargs):# TODOresult = fn(*args, **kwargs)return resultreturn inner
  • 函数 inner() 接受任意数量的位置参数和关键字参数。
  • 调用原始函数 fn 并返回其结果。

2.7 带有参数的装饰器

带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,...)

  • 代码示例
def logger(flag):"""带有参数的装饰器。:param flag::return:"""def decorator(fn):def inner(a, b):# 根据装饰器参数,有不同的日志提示功能。if flag == "+":print("--- 开始加法计算 ---")elif flag == "-":print("--- 开始减法计算 ---")else:raise Exception("参数不合法!")return fn(a, b)return innerreturn decorator@logger("+")
def my_sum(a, b):return a + b@logger("-")
def my_sub(a, b):return a - bif __name__ == '__main__':print(my_sum(3, 6))# --- 开始加法计算 ---# 9print(my_sub(6, 3))# --- 开始减法计算 ---# 3

2.8 类装饰器

装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。

  • 类装饰器就是使用类来实现的装饰器。它们通常通过在类中定义 __call__() 方法来实现。
  • 当我们使用 @ 语法应用装饰器时,Python 会调用装饰器类的 __init__() 方法创建一个实例,然后将被装饰的函数或类作为参数传递给 __init__() 方法。
  • 当被装饰的函数或方法被调用时,Python 会调用装饰器实例的 __call__() 方法。
  • 代码示例
class Logger(object):def __init__(self, fn):# 将被装饰的函数 fn 保存在实例属性 fn 中。self.__fn = fndef __call__(self, *args, **kwargs):"""实现__call__方法,表示把类像调用函数一样进行调用。:param args::param kwargs::return:"""# 添加装饰功能。print("开始记录日志信息...")self.__fn()@Logger
def login():print("完成登录")if __name__ == '__main__':login()# 开始记录日志信息...# 完成登录
  • 调用 login() 方法时,本质上是在调用 __call__() 方法。
  • 相比函数装饰器,类装饰器有几个主要优势
    • 更好的组织:类装饰器可以利用 Python 的面向对象特性,将相关的方法和数据封装在一起,这使得代码更易于理解和维护。
    • 更大的灵活性:类装饰器可以利用继承来复用和扩展代码。例如,你可以创建一个基础的装饰器类,然后通过继承这个类来创建特定的装饰器。
    • 更好的控制:类装饰器可以使用实例变量来保存状态。这在一些需要保存状态的装饰器(例如计数器或缓存)中非常有用。

2.9 装饰器的使用场景

  • 统计程序的执行时间:可以通过编写一个装饰器来记录函数的开始时间和结束时间,然后计算函数执行的时间。这个装饰器可以被用于优化程序性能或者进行调试。
import timedef time_cost(fn):def inner():begin = time.time()fn()# 当前时间减去开始时间。cost = time.time() - beginprint(f"函数执行花费={cost}s")return inner@time_cost
def sample():# 线程睡眠 2s。time.sleep(2)if __name__ == '__main__':sample()# 函数执行花费=2.003966808319092s
  • 辅助系统功能输出日志信息:可以编写一个装饰器来记录函数的执行过程以及输出日志信息,这个装饰器可以被用于调试、监控和错误处理等方面。
def logger(fn):def inner(id):print("开始记录日志信息...")print(f"用户查询id={id}")result = fn(id)print(f"用户查询结果={result}")print(f"日志记录完成,准备返回结果!")return resultreturn inner@logger
def query(user_id):msg = {}if user_id is not None:msg = {"user": "xxx","age": 18}return msgif __name__ == '__main__':query(111)# 开始记录日志信息...# 用户查询id=111# 用户查询结果={'user': 'xxx', 'age': 18}# 日志记录完成,准备返回结果!

三、装饰器总结

  1. Python 的装饰器和 Java 的注解(Annotation并不是同一回事,和 C# 中的特性(Attribute)也不一样,完全是两个概念。
  2. 装饰器的理念是对原函数、对象的加强,相当于重新封装,所以一般装饰器函数都被命名为 wrapper(),意义在于包装。函数只有在被调用时才会发挥其作用。比如 @logging 装饰器可以在函数执行时额外输出日志,@cache 装饰过的函数可以缓存计算结果等等。
  3. 而注解和特性则是对目标函数或对象添加一些属性,相当于将其分类。这些属性可以通过反射拿到,在程序运行时对不同的特性函数或对象加以干预。比如带有 Setup 的函数就当成准备步骤执行,或者找到所有带有TestMethod 的函数依次执行等等。

四、结束语


“-------怕什么真理无穷,进一寸有一寸的欢喜。”

微信公众号搜索:饺子泡牛奶

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

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

相关文章

知识蒸馏代码实现(以MNIST手写数字体为例,自定义MLP网络做为教师和学生网络)

dataloader_tools.py import torchvision from torchvision import transforms from torch.utils.data import DataLoaderdef load_data():# 载入MNIST训练集train_dataset torchvision.datasets.MNIST(root "../datasets/",trainTrue,transformtransforms.ToTens…

ES6中的类

1、Class 类是一种可选(而不是必须)的设计模式,而且在 JavaScript 这样的 [[Prototype]] 语言中实现类是很别扭的。大致解决了以下问题: 不再引用杂乱的 .prototype 了Button 声 明 时 直 接“ 继 承 ” 了 Widget, …

Docker容器化平台及其优势和应用场景介绍

Docker是一种开源的容器化平台,它基于操作系统级别虚拟化技术,可以将应用程序及其依赖项打包成一个独立的容器,提供轻量级、一致性、可移植性的应用环境。Docker的基本概念和优势如下: 镜像(Image):Docker容器的基础&…

JAVA对象转HashMap如何快速强转

小编我最近看到了些资料,之前JAVA对象转DTO都是另外写类&#xff0c;进行强转&#xff0c;里面有些Long类型&#xff0c;日期类型&#xff0c;都是转成String类型&#xff0c;现在有快速优雅得解决方式 我们需要得Map结构是 Map<String,object> 简单说明一下这个类&…

QT Day01 qt概述,创建项目,窗口属性,按钮,信号与槽

1.qt概述 1.什么是qt Qt 是一个跨平台的 C 图形用户界面应用程序框架。它为应用程序开发者提供建立艺 术级图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正的组 件编程。 2.支持的平台 Windows – XP 、 Vista 、 Win7 、 Win8…

Java(119):ExcelUtil工具类(org.apache.poi读取和写入Excel)

ExcelUtil工具类(XSSFWorkbook读取和写入Excel),入参和出参都是:List<Map<String,Object>> 一、读取Excel testdata.xlsx 1、new XSSFWorkbook对象 File file = new File(filePath); FileInputStream fis = new FileInputStream(file);…

8.二维数组——将一个二维数组行和列的元素互换,存到另一个二维数组中。

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 前言 本系列为二维数组编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 将一个二维数组行和列的元素互换&#xff0c;存到另一个二维数组中。 二、题目分析 三、解题 程序运行代码 #incl…

玄学调参实践篇 | 深度学习模型 + 预训练模型 + 大模型LLM

&#x1f60d; 这篇主要简单记录一些调参实践&#xff0c;无聊时会不定期更新~ 文章目录 0、学习率与batch_size判断1、Epoch数判断2、判断模型架构是否有问题3、大模型 - 计算量、模型、和数据大小的关系4、大模型调参相关论文经验总结5、训练时模型的保存 0、学习率与batch_s…

python中几次方怎么打,三种内置方法

Python中几次方的三种内置方法 Python中至少内置的三种可以用于求取某个底数的几次方的方法&#xff0c;如下&#xff1a; 第一种方法&#xff0c;通过Python内置的幂次方运算符“**”&#xff1b;使用math模块的pow()方法&#xff0c;可以用于求取幂次方&#xff0c;即pow()…

压力测试+接口测试

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简单。因 为jmeter是java开发的&#xff0c;所以运行的时候必须先要安装jdk才可以。jmeter是…

鸿蒙系统开发手册 - HarmonyOS内核驱动层源码分析

众所周知系统定义HarmonyOS是一款“面向未来”、面向全场景&#xff08;移动办公、运动健康、社交通信、媒体娱乐等&#xff09;的分布式操作系统。在传统的单设备系统能力的基础上&#xff0c;HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念&#xff0c;能…

Arrays.asList() 与 Collections.singletonList()的恩怨情仇

1. 概述 列表是我们使用 Java 时常用的集合类型。 众所周知&#xff0c;我们可以轻松地用一行初始化一个List。例如&#xff0c;当我们想要初始化一个只有一个元素的List时&#xff0c;我们可以使用Arrays.asList()方法或Collections.singletonList()方法。 在本文中&#x…

【Linux】基础IO--文件基础知识/文件操作/文件描述符

文章目录 一、文件相关基础知识二、文件操作1.C语言文件操作2.操作系统文件操作2.1 比特位传递选项2.2 文件相关系统调用2.3 文件操作接口的使用 三、文件描述符fd1.什么是文件描述符2.文件描述符的分配规则 一、文件相关基础知识 我们对文件有如下的认识&#xff1a; 1.文件 …

用最少数量的箭引爆气球[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 有一些球形气球贴在一堵用XY平面表示的墙面上。墙面上的气球记录在整数数组points&#xff0c;其中points[i] [xstart, xend]表示水平直径在xstart和xend之间的气球。你不知道气球的确切y坐标。一支弓箭可以沿着x轴从不同点完全垂直…

vue-动态组件、keep-alive

vue-动态组件、keep-alive 如果我们想写一个tabbar导航栏&#xff0c;我能想到的两种方式 通过if条件判断的方式实现&#xff08;不赘述&#xff09;动态组件 接下来我们就看看动态组件如何创建&#xff0c;废话不多少直接上代码&#xff08;代码中有备注&#xff09; 首先…

Panalog 日志审计系统 前台RCE漏洞复现

0x01 产品简介 Panalog是一款日志审计系统&#xff0c;方便用户统一集中监控、管理在网的海量设备。 0x02 漏洞概述 Panalog日志审计系统 sy_query.php接口处存在远程命令执行漏洞&#xff0c;攻击者可执行任意命令&#xff0c;接管服务器权限。 0x03 复现环境 FOFA&#xf…

谭巍主任专业角度解读:疣体脱落前的症状是什么?

我们时常会发现身体各个部位长出一些赘生物&#xff0c;有些属于皮肤良性改变&#xff0c;而有些则是病毒引起的&#xff0c;称之为疣体。然而在疣体脱落之前&#xff0c;通常会出现一些症状&#xff0c;这些症状可能因人而异&#xff0c;但以下是一些常见的迹象&#xff1a; 1…

如何隐藏选择选项值并用新值替换2个选项?

要隐藏选择选项值并用新值替换2个选项&#xff0c;可以使用JavaScript来实现。 首先&#xff0c;使用JavaScript获取两个选项的值&#xff0c;然后将这两个值设置为新的值&#xff0c;最后将这两个选项的可见性设置为false&#xff0c;以隐藏它们。 例如&#xff1a; <se…

笔记61:注意力提示

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a

MySQL索引使用总结

索引(index) 官方定义&#xff1a;一种提高MySQL查询效率的数据结构 优点&#xff1a;加快查询速度 缺点&#xff1a; 1.维护索引需要消耗数据库资源 2.索引需要占用磁盘空间 3.增删改的时候会影响性能 索引分类 索引和数据库表的存储引擎有关&#xff0c;不同的存储引擎&am…