闭包、装饰器学习笔记(第二次学习)
以下是整理后的笔记格式:
---
# 闭包与装饰器
## 一、闭包的概念,作用,条件### 作用:
1. **保存外部函数的变量**:可以让一个变量常驻于内存。
```python
def func():a = 10def inner():print(a)return areturn innerret = func()
```
`inner` 函数的执行时间不确定,但必须保证 `inner` 里面需要使用的变量等,必须常驻内存。
**应用:简单的计数器**
```python
def func():a = 10def inner():nonlocal aa = a + 1return areturn innerret = func()r1 = ret()print(r1)r2 = ret()print(r2)r3 = ret()print(r3)
```
结果:11 12 13
2. **避免全局变量被修改**:避免设置全局变量,进而避免了全局变量被随意修改。(更安全)
### 闭包本质:
- 内层函数对外层函数的局部变量的使用。此时内层函数被称为闭包函数。
1. 可以让一个变量常驻与内存
2. 可以避免全局变量被修改
---
## 二、装饰器
### 装饰器本质上是一个闭包
### 作用:
- 在不改变原有函数调用的情况下,给函数增加新的功能。
- 直白:可以在函数前后添加新功能,但是不改原来的代码
### 通用装饰器写法:
```python
def wrapper(fn): # wrapper: 装饰器, fn: 目标函数def inner(*args, **kwargs): # 接受任意参数和关键字参数# 在目标函数执行之前……print("在目标函数执行之前……")ret = fn(*args, **kwargs) # 调用原始函数,并保存返回值# 在目标函数执行之后……return ret # 返回原始函数的返回值return inner
```
### 预备知识:
1. **函数可以作为参数传递**
```python
def aaa():print("我是aaa,我执行了!————————")def bbb(func):print("我是bbb")func()print("我还是bbb")bbb(aaa)
```
结果:
```
我是bbb
我是aaa,我执行了!————————
我还是bbb
```2. **函数名可以作为返回值返回**
```python
def func():a = 10def inner():nonlocal aa = a + 1return areturn innerret = func()result = ret()print(result)result = ret()print(result)result = ret()print(result)
```
### 装饰器形成历程:####
###初始版本:正常玩游戏
```python
def play_dnf():print("你好啊,我叫赛利亚,今天又是美好的一天!")def play_lol():print("德玛西亚!!!!!")play_dnf()
play_lol()
```
结果:
```
你好啊,我叫赛利亚,今天又是美好的一天!
德玛西亚!!!!!
```#### 版本2:我想开外挂
```python
def play_dnf():print("你好啊,我叫赛利亚,今天又是美好的一天!")def play_lol():print("德玛西亚!!!!!")print("开外挂")
play_dnf()
print("关外挂")print("开外挂")
play_lol()
print("关外挂")
```
结果:
```
开外挂
你好啊,我叫赛利亚,今天又是美好的一天!
关外挂
开外挂
德玛西亚!!!!!
关外挂
```#### 版本3:我不想频繁开关外挂,想雇一个管家帮我操作。
```python
def guanjia(game): # 管家要足够智能,根据我玩什么游戏开外挂print("开外挂")game()print("关外挂")def play_dnf():print("你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!!")def play_lol():print("德玛西亚!!!!玩LOL!")guanjia(play_dnf) # 传入玩的游戏
guanjia(play_lol)
```
结果:
```
开外挂
你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!!
关外挂
开外挂
德玛西亚!!!!玩LOL!
关外挂
```#### 终极版本之前:上面是管家在玩游戏,应该是我玩。
```python
def guanjia(game):def inner():print("开始游戏")game()print("游戏结束")return innerdef play_dnf():print("你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!!")def play_lol():print("德玛西亚!!!!玩LOL!")play_dnf = guanjia(play_dnf) # 让管家把游戏重新封装一遍,这边把原来的游戏替换了
play_lol = guanjia(play_lol)play_dnf() # 此时运行的是管家给的内层函数Inner
play_dnf() # 此时运行的是管家给的内层函数Inner
play_dnf() # 此时运行的是管家给的内层函数Inner
play_dnf() # 此时运行的是管家给的内层函数Inner
```#### 终极版本: 用装饰器
```python
def guanjia(game):def inner():print("开始游戏")game()print("游戏结束")return inner@guanjia # 相当于 play_dnf = guanjia(play_dnf)
def play_dnf():print("你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!!")@guanjia # 相当于 play_lol = guanjia(play_lol)
def play_lol():print("德玛西亚!!!!玩LOL!")play_dnf()
play_lol()
```### 带参数版本:
```python
def guanjia(game):def inner(*args, **kwargs): # 这里的 * 和 ** 代表接收参数,打包成元组和字典print("开始游戏")game(*args, **kwargs) # 这里的 * 和 ** 代表把接收的元组和字典解包成位置参数和关键字参数print("游戏结束")return inner@guanjia # 相当于 play_dnf = guanjia(play_dnf)
def play_dnf(username, password):print("你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!!", username, password)@guanjia # 相当于 play_lol = guanjia(play_lol)
def play_lol(username, password, hero):print("德玛西亚!!!!玩LOL!", username, password, hero)play_dnf("admin", "123456")
play_lol("admin2", "123456", "大盖伦")
```
### 想获得返回值:
```python
def play_dnf(username, password):
print("你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!!", username, password)
return "一把屠龙刀"
ret = play_dnf("admin", "123456")
print("获得道具:", ret)
```
结果:
```
你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!! admin 123456
获得道具: 一把屠龙刀
```### 使用装饰器获得返回值:
```python
def guanjia(game):def inner(*args, **kwargs):print("开始游戏")ret = game(*args, **kwargs) # 这里是目标函数执行,需要有返回值。print("游戏结束")return retreturn inner@guanjia
def play_dnf(username, password):print("你好啊,我叫赛利亚,今天又是美好的一天!玩DNF!!!", username, password)return "一把屠龙刀"ret = play_dnf("admin", "123456")
print("获得道具:", ret)
#一个函数被多个装饰器装饰:
#一个函数被多个装饰器装饰:def wrapper1(fn):def inner(*args, **kwargs):print("这里是wrapper1 进入")ret = fn(*args, **kwargs)print("这里是wrapper1 出去")return retreturn innerdef wrapper2(fn):def inner(*args, **kwargs):print("这里是wrapper2 进入")ret = fn(*args, **kwargs)print("这里是wrapper2 出去")return retreturn inner
@wrapper1 # target = wrapper1(wrapper2.inner)==>target: wrapper1.inner
@wrapper2 # target = wrapper2(target) => target: wrapper2.inner
def target():print('我是目标')target()
执行过程:wrapper1->wrapper2->target-->wrapper2-->wrapper1
结果:
这里是wrapper1 进入
这里是wrapper2 进入
我是目标
这里是wrapper2 出去
这里是wrapper1 出去
实战:完成登录验证装饰器:
login_flag = False def login_verify(fn):def inner(*args, **kwargs):global login_flagif login_flag == False:# 这里完成登录校验print('还未完成用户登录操作')while 1:username = input(">>>")password = input(">>>")if username == "admin" and password == "123":print("登录成功")login_flag = Truebreakelse:print("登录失败,用户名或密码错误")ret = fn(*args, **kwargs) # 后续程序的执行return retreturn inner@login_verify
def add():print("添加员工信息")@login_verify
def delete():print("删除员工信息")@login_verify
def upd():print("更新员工信息")@login_verify
def search():print("搜索员工信息")add()
delete()
upd()
search()
结果:
还未完成登录验证操作!
>>>admin
>>>123登录成功
添加员工信息
删除员工信息
更新员工信息
搜索员工信息
代码分析:
这段代码定义了一个装饰器 `login_verify`,用于模拟登录验证功能。装饰器内部定义了一个 `inner` 函数,该函数首先检查全局变量 `login_flag` 是否为 `False`。如果是,它会提示用户进行登录,并要求输入用户名和密码。如果用户名为 "admin" 且密码为 "123",则将 `login_flag` 设置为 `True` 并跳出循环,表示登录成功。否则,提示登录失败。
装饰器 `login_verify` 被应用于四个函数 `add`、`delete`、`upd` 和 `search`,这些函数分别用于添加、删除、更新和搜索员工信息。当这些函数被调用时,它们会首先执行 `login_verify` 装饰器中的代码,即进行登录验证。如果验证通过(即 `login_flag` 为 `True`),则继续执行函数本身的操作。
以下是代码执行的详细解析:
1. 定义全局变量 `login_flag` 并设置为 `False`,表示初始时未登录。
2. 定义装饰器 `login_verify`,它接受一个函数 `fn` 作为参数。
3. 在 `login_verify` 内部定义 `inner` 函数,该函数接受任意参数和关键字参数。
4. 在 `inner` 函数中,使用 `global` 关键字声明 `login_flag` 为全局变量,以便在函数内部修改其值。
5. 如果 `login_flag` 为 `False`,则进入登录验证流程,提示用户输入用户名和密码,并进行校验。如果校验成功(用户名为 "admin" 且密码为 "123"),则打印 "登录成功" 并将 `login_flag` 设置为 `True`,然后跳出循环。
6. 如果登录验证通过,执行被装饰的函数 `fn` 并返回其返回值。
7. 将 `login_verify` 装饰器应用于 `add`、`delete`、`upd` 和 `search` 函数,这些函数在执行前都会进行登录验证。
8. 依次调用 `add`、`delete`、`upd` 和 `search` 函数。由于 `login_flag` 初始为 `False`,每次调用这些函数时都会触发登录验证流程。只有当输入正确的用户名和密码后,才会执行相应的函数操作。
这段代码通过装饰器实现了一个简单的登录验证机制,确保在执行特定操作前用户已经通过验证。
相关文章:
闭包、装饰器学习笔记(第二次学习)
以下是整理后的笔记格式: --- # 闭包与装饰器 ## 一、闭包的概念,作用,条件### 作用: 1. **保存外部函数的变量**:可以让一个变量常驻于内存。 python def func():a 10def inner():print(a)return areturn inne…...
Windows下docker使用教程
docker安装 镜像制作镜像加载容器创建更新镜像导出镜像 Windows10安装dockerdocker image制作docker 镜像加载docker 容器创建更新imageimage 导出为.tar文件 #以Windows10 、11为例 linux和Windows区别在于docker安装的程序是哪个操作系统的,后面的内容其实不变 …...
Java项目生成接口文档的方案
文章目录 问题:Java项目生成接口文档的方案方案一:Swagger3.0方案二:Apipost两者对比 问题:Java项目生成接口文档的方案 需求 1、需要生成生成时间,作者名称,项目名称,接口名称,请…...
Android第七次面试总结(Java和kotlin源码级区别 )
Java 和 Kotlin 作为用于软件开发尤其是 Android 和后端开发的编程语言,在源码层面存在诸多区别,下面从多个方面进行深入讲解: 1. 基础语法与变量声明 变量声明方式 Java:变量声明时必须明确指定数据类型,并且若要声…...
北斗导航 | 改进奇偶矢量法的接收机自主完好性监测算法原理,公式,应用,RAIM算法研究综述,matlab代码
改进奇偶矢量法的接收机自主完好性监测算法研究 摘要 接收机自主完好性监测(RAIM)是保障全球导航卫星系统(GNSS)安全性的核心技术。针对传统奇偶矢量法在噪声敏感性、多故障隔离能力上的缺陷,本文提出一种基于加权奇偶空间与动态阈值的改进算法。通过引入观测值权重矩阵重…...
案例实践 | 招商局集团以长安链构建“基于DID的航运贸易数据资产目录链”
概览 案例名称 基于DID的航运贸易数据资产目录链 业主单位 招商局集团 上线时间 2024年10月 用户群体 供数用数企业和个人 用户规模 集团内20企业 案例背景 招商局集团深入落实“促进数据高效流通使用、赋能实体经济”精神,深化集团数字化水平,…...
【C++】httplib:轻量级的 HTTP 服务器和客户端
本教程采用渐进式讲解方式,适用于 MinGW 环境。我们将从 httplib 的基本概念入手,通过一个小型 Demo 展示其核心用法,最后深入探讨高级功能与实际应用场景。 1. 简介 1.1 httplib 基本概念 httplib 是一个轻量级的 C HTTP 库,设…...
【算法工程】RAG:针对linux下文档解析出现乱码问题的解决
RAG服务中,非常关键的模块是文档解析。但将解析服务部署到linux平台,解析word、ppt等文档时可能就会出现乱码,核心原因是系统未能识别出对应的字体。因为word、ppt在windows下是最适配的,如果将解析服务部署到linux上,…...
亚马逊云科技全面托管DeepSeek-R1模型现已上线
文章目录 亚马逊云科技全面托管DeepSeek-R1模型现已上线在Amazon Bedrock中开始使用DeepSeek-R1模型DeepSeek-R1现已可用 亚马逊云科技全面托管DeepSeek-R1模型现已上线 亚马逊云科技提供众多免费云产品,可以访问:亚马逊云科技 截至1月30日,D…...
2025年移动端开发性能优化实践与趋势分析
启动速度优化 本质:缩短首次可见帧渲染时间。 方法: iOS:利用Core ML本地模型轻量化部署,减少云端等待。Android:强制启用SplashScreen API,通过setKeepOnScreenCondition控制动画时长。冷启动需将耗时操…...
Docker Compose介绍
基本概念 Docker-Compose是Docker官方的开源项目,负责实现对docker容器集群的快速编排。 可以这么理解,docker compose是docker提出的一个工具软件,可以管理多个docker容器组成一个应用,只需要编写一个YAML格式的配置文件docker…...
openGauss关联列数据类型不一致引起谓词传递失败
今天分享一个比较有意思的案例 注意:因为原始SQL很长,为了方便排版,简化了SQL 下面SQL跑60秒才出结果,客户请求优化 select dtcs.owner, dtcs.table_name, dtcs.column_name, dct.commentsfrom dba_tab_columns dtcsleft outer j…...
头歌实践教学平台--【数据库概论】--SQL
一、表结构与完整性约束的修改(ALTER) 1.修改表名 USE TestDb1; alter table your_table rename TO my_table; 2.添加与删除字段 #语句1:删除表orderDetail中的列orderDate alter table orderDetail drop orderDate; #语句2:添加列unitPrice alter t…...
Unity 全栈开发商业级 MMORPG 大型网游:源码与课件助力进阶之路
Unity 全栈开发商业级 MMORPG 大型网游:源码与课件助力进阶之路 在竞争激烈的游戏市场中,大型多人在线角色扮演游戏(MMORPG)凭借其丰富的世界观、庞大的玩家社区以及持续的内容更新,始终占据着重要地位。Unity 作为一…...
软件工程面试题(六)
1、forward及redirect 的区别?有哪些方式实现 <jsp:forward>重定向后url地址栏地址不变还是原来的地址;而response.sendRedirect()重定向后url地址栏地址显示的请求后的新地址。<jsp:forward>重定向的时候可以保存回话信息,因此可以使用re…...
Apache Dubbo 与 ZooKeeper 集成:服务注册与发现的全解析
在分布式系统中,Apache Dubbo 作为一个高性能的 RPC 和微服务框架,广泛用于服务治理,而 ZooKeeper 作为其常用注册中心,提供了服务注册与发现的核心能力。在2025年的技术生态中,理解 Dubbo 与 ZooKeeper 的集成原理和使…...
算法基础——模拟
目录 1 多项式输出 2.蛇形方阵 3.字符串的展开 模拟,顾名思义,就是题⽬让你做什么你就做什么,考察的是将思路转化成代码的代码能⼒。这类题⼀般较为简单,属于竞赛⾥⾯的签到题(但是,万事⽆绝对ÿ…...
【第30节】MFC编程:ListCtrl控件和TreeCtrl控件
目录 引言 一、高级控件ListCtrl 二、高级控件TreeCtrl 三、Shell控件 四、CImageList 五、综合代码示例 引言 在MFC编程里,高级控件能大幅提升应用程序的交互性与功能性。接下来,咱们会详细讲讲ListCtrl和TreeCtrl这两个高级控件。不仅会介绍它们…...
kotlin知识体系(四) : inline、noinline、crossinline 关键字对应编译后的代码是怎样的 ?
1. inline、noinline、crossinline 的作用 在 Kotlin 里,inline、noinline 和 crossinline 这几个关键字和高阶函数紧密相关,它们能够对高阶函数的行为进行优化和控制。本文接下来会详细介绍它们的作用和原理。 1.1 inline 关键字 inline 关键字用于修…...
JavaScript 手写 call、apply、bind 和 new
1. 手写 call 方法 核心思路:改变函数的 this 指向并立即执行,通过将函数临时挂载到目标对象上调用。 Function.prototype.myCall function (context, ...args) {// 如果 context 为 null 或 undefined,则默认为 windowcontext context |…...
睡眠健康领域的智能硬件设备未来的发展趋势
随着社会节奏的不断加快,人们的睡眠问题愈发多了起来,主要表现有以下几个方面: 睡眠质量下降 浅睡眠增多:现代生活中,人们面临着各种压力源,如工作压力、生活琐事、经济压力等,这些压力会导致大…...
计算机网络基础:量子通信技术在网络中的应用前景
计算机网络基础:量子通信技术在网络中的应用前景 一、前言二、量子通信技术基础2.1 量子通信的基本概念2.2 量子通信的主要原理2.2.1 量子密钥分发(QKD)原理2.2.2 量子隐形传态原理三、量子通信技术的特点3.1 绝对安全性3.2 超高通信速率潜力3.3 抗干扰能力强四、量子通信技…...
Postman 下载文件指南:如何请求 Excel/PDF 文件?
在 Postman 中进行 Excel/PDF 文件的请求下载和导出,以下是简明的步骤,帮助你轻松完成任务。首先,我们将从新建接口开始,逐步引导你完成整个过程。 Postman 请求下载/导出 excel/pdf 文件教程...
Stereolabs ZED Box Mini:机器人与自动化领域的人工智能视觉新选择
在人工智能视觉技术快速发展的今天,其应用场景正在持续拓宽,从智能安防到工业自动化,从机器人技术到智能交通,各领域都在积极探索如何利用这一先进技术。而 Stereolabs 推出的ZED Box Mini,正是一款专为满足这些多样化…...
arm之s3c2440的I2C的用法
基础概念 IC(Inter-Integrated Circuit)又称I2C,是是IICBus简称,所以中文应该叫集成电路总线。 IIC的总线的使用场景,所有挂载在IIC总线上的设备都有两根信号线,一根是数据线SDA,另一 根是时钟…...
安装node,配置npm, yarn, pnpm, bun
文章目录 安装node, 配置 npm, yarn, pnpm, bun配置node配置 npm, yarn, pnpm, bunnpmyarnpnpmbun 安装node, 配置 npm, yarn, pnpm, bun 配置node 输入网址:Node.js,包含各种安装方式以及多版本管理方式。也可以直接下载安装包。 安装包的安装过程…...
redis部署架构
一.redis多实例 如上图所示,我们经常使用实例的端口号来作为实例的安装目录名称。 1.创建实例安装目录 如上图所示,这是创建实例的安装目录, 2.拷贝实例的配置文件 如上图所示,将redis解压目录下的配置文件拷贝到对应的conf目录…...
深入理解指针(4)(C语言版)
文章目录 前言一、回调函数是什么(一)定义(二)工作原理(三)应用场景 二、qsort举例(一)qsort函数简介(二)比较函数的定义(三)使用示例…...
【HTML】验证与调试工具
个人主页:Guiat 归属专栏:HTML CSS JavaScript 文章目录 1. HTML 验证工具概述1.1 验证的重要性1.2 常见 HTML 错误类型 2. W3C 验证服务2.1 W3C Markup Validation Service2.2 使用 W3C 验证器2.3 验证结果解读 3. 浏览器开发者工具3.1 Chrome DevTools…...
【Mysql】SQL 优化全解析
文章目录 一、理解执行计划1.1 执行计划的作用1.2 查看执行计划 二、查询优化2.1 避免全表扫描2.2 使用覆盖索引2.3 合理使用 JOIN 三、索引优化3.1 索引设计原则3.2 索引维护 在数据驱动的当今时代,MySQL 作为应用广泛的开源关系型数据库&…...
