当前位置: 首页 > article >正文

闭包、装饰器学习笔记(第二次学习)

以下是整理后的笔记格式:

---

# 闭包与装饰器

## 一、闭包的概念,作用,条件### 作用:
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企业 案例背景 招商局集团深入落实“促进数据高效流通使用、赋能实体经济”精神,深化集团数字化水平&#xff0c…...

【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地址栏地址不变还是原来的地址&#xff1b;而response.sendRedirect()重定向后url地址栏地址显示的请求后的新地址。<jsp:forward>重定向的时候可以保存回话信息&#xff0c;因此可以使用re…...

Apache Dubbo 与 ZooKeeper 集成:服务注册与发现的全解析

在分布式系统中&#xff0c;Apache Dubbo 作为一个高性能的 RPC 和微服务框架&#xff0c;广泛用于服务治理&#xff0c;而 ZooKeeper 作为其常用注册中心&#xff0c;提供了服务注册与发现的核心能力。在2025年的技术生态中&#xff0c;理解 Dubbo 与 ZooKeeper 的集成原理和使…...

算法基础——模拟

目录 1 多项式输出 2.蛇形方阵 3.字符串的展开 模拟&#xff0c;顾名思义&#xff0c;就是题⽬让你做什么你就做什么&#xff0c;考察的是将思路转化成代码的代码能⼒。这类题⼀般较为简单&#xff0c;属于竞赛⾥⾯的签到题&#xff08;但是&#xff0c;万事⽆绝对&#xff…...

【第30节】MFC编程:ListCtrl控件和TreeCtrl控件

目录 引言 一、高级控件ListCtrl 二、高级控件TreeCtrl 三、Shell控件 四、CImageList 五、综合代码示例 引言 在MFC编程里&#xff0c;高级控件能大幅提升应用程序的交互性与功能性。接下来&#xff0c;咱们会详细讲讲ListCtrl和TreeCtrl这两个高级控件。不仅会介绍它们…...

kotlin知识体系(四) : inline、noinline、crossinline 关键字对应编译后的代码是怎样的 ?

1. inline、noinline、crossinline 的作用 在 Kotlin 里&#xff0c;inline、noinline 和 crossinline 这几个关键字和高阶函数紧密相关&#xff0c;它们能够对高阶函数的行为进行优化和控制。本文接下来会详细介绍它们的作用和原理。 1.1 inline 关键字 inline 关键字用于修…...

JavaScript 手写 call、apply、bind 和 new

1. 手写 call 方法 核心思路&#xff1a;改变函数的 this 指向并立即执行&#xff0c;通过将函数临时挂载到目标对象上调用。 Function.prototype.myCall function (context, ...args) {// 如果 context 为 null 或 undefined&#xff0c;则默认为 windowcontext context |…...

睡眠健康领域的智能硬件设备未来的发展趋势

随着社会节奏的不断加快&#xff0c;人们的睡眠问题愈发多了起来&#xff0c;主要表现有以下几个方面&#xff1a; 睡眠质量下降 浅睡眠增多&#xff1a;现代生活中&#xff0c;人们面临着各种压力源&#xff0c;如工作压力、生活琐事、经济压力等&#xff0c;这些压力会导致大…...

计算机网络基础:量子通信技术在网络中的应用前景

计算机网络基础:量子通信技术在网络中的应用前景 一、前言二、量子通信技术基础2.1 量子通信的基本概念2.2 量子通信的主要原理2.2.1 量子密钥分发(QKD)原理2.2.2 量子隐形传态原理三、量子通信技术的特点3.1 绝对安全性3.2 超高通信速率潜力3.3 抗干扰能力强四、量子通信技…...

Postman 下载文件指南:如何请求 Excel/PDF 文件?

在 Postman 中进行 Excel/PDF 文件的请求下载和导出&#xff0c;以下是简明的步骤&#xff0c;帮助你轻松完成任务。首先&#xff0c;我们将从新建接口开始&#xff0c;逐步引导你完成整个过程。 Postman 请求下载/导出 excel/pdf 文件教程...

Stereolabs ZED Box Mini:机器人与自动化领域的人工智能视觉新选择

在人工智能视觉技术快速发展的今天&#xff0c;其应用场景正在持续拓宽&#xff0c;从智能安防到工业自动化&#xff0c;从机器人技术到智能交通&#xff0c;各领域都在积极探索如何利用这一先进技术。而 Stereolabs 推出的ZED Box Mini&#xff0c;正是一款专为满足这些多样化…...

arm之s3c2440的I2C的用法

基础概念 IC&#xff08;Inter-Integrated Circuit&#xff09;又称I2C&#xff0c;是是IICBus简称&#xff0c;所以中文应该叫集成电路总线。 IIC的总线的使用场景&#xff0c;所有挂载在IIC总线上的设备都有两根信号线&#xff0c;一根是数据线SDA&#xff0c;另一 根是时钟…...

安装node,配置npm, yarn, pnpm, bun

文章目录 安装node, 配置 npm, yarn, pnpm, bun配置node配置 npm, yarn, pnpm, bunnpmyarnpnpmbun 安装node, 配置 npm, yarn, pnpm, bun 配置node ​ 输入网址&#xff1a;Node.js&#xff0c;包含各种安装方式以及多版本管理方式。也可以直接下载安装包。 安装包的安装过程…...

redis部署架构

一.redis多实例 如上图所示&#xff0c;我们经常使用实例的端口号来作为实例的安装目录名称。 1.创建实例安装目录 如上图所示&#xff0c;这是创建实例的安装目录&#xff0c; 2.拷贝实例的配置文件 如上图所示&#xff0c;将redis解压目录下的配置文件拷贝到对应的conf目录…...

深入理解指针(4)(C语言版)

文章目录 前言一、回调函数是什么&#xff08;一&#xff09;定义&#xff08;二&#xff09;工作原理&#xff08;三&#xff09;应用场景 二、qsort举例&#xff08;一&#xff09;qsort函数简介&#xff08;二&#xff09;比较函数的定义&#xff08;三&#xff09;使用示例…...

【HTML】验证与调试工具

个人主页&#xff1a;Guiat 归属专栏&#xff1a;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 索引维护​ 在数据驱动的当今时代&#xff0c;MySQL 作为应用广泛的开源关系型数据库&…...