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

news2025/3/31 13:16:36

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

---

# 闭包与装饰器

## 一、闭包的概念,作用,条件### 作用:
1. **保存外部函数的变量**:可以让一个变量常驻于内存。
   ```python

   def func():
       a = 10
       def inner():
           print(a)
           return a
       return inner

   ret = func()


   ```
   `inner` 函数的执行时间不确定,但必须保证 `inner` 里面需要使用的变量等,必须常驻内存。

**应用:简单的计数器**
   ```python
 

 def func():
       a = 10
       def inner():
           nonlocal a
           a = a + 1
           return a
       return inner

   ret = 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 = 10
       def inner():
           nonlocal a
           a = a + 1
           return a
       return inner

   ret = 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 inner

def 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 ret
    return 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 ret
    return inner

def wrapper2(fn):
    def inner(*args, **kwargs):
        print("这里是wrapper2 进入")
        ret = fn(*args, **kwargs)
        print("这里是wrapper2 出去")
        return ret
    return 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_flag
        if login_flag == False:
            # 这里完成登录校验
            print('还未完成用户登录操作')
            while 1:
                username = input(">>>")
                password = input(">>>")
                if username == "admin" and password == "123":
                    print("登录成功")
                    login_flag = True
                    break
                else:
                    print("登录失败,用户名或密码错误")

        ret = fn(*args, **kwargs)  # 后续程序的执行
        return ret
    return 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`,每次调用这些函数时都会触发登录验证流程。只有当输入正确的用户名和密码后,才会执行相应的函数操作。

这段代码通过装饰器实现了一个简单的登录验证机制,确保在执行特定操作前用户已经通过验证。

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

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

相关文章

Windows下docker使用教程

docker安装 镜像制作镜像加载容器创建更新镜像导出镜像 Windows10安装dockerdocker image制作docker 镜像加载docker 容器创建更新imageimage 导出为.tar文件 #以Windows10 、11为例 linux和Windows区别在于docker安装的程序是哪个操作系统的,后面的内容其实不变 …

Java项目生成接口文档的方案

文章目录 问题:Java项目生成接口文档的方案方案一:Swagger3.0方案二:Apipost两者对比 问题:Java项目生成接口文档的方案 需求 1、需要生成生成时间,作者名称,项目名称,接口名称,请…

案例实践 | 招商局集团以长安链构建“基于DID的航运贸易数据资产目录链”

概览 案例名称 基于DID的航运贸易数据资产目录链 业主单位 招商局集团 上线时间 2024年10月 用户群体 供数用数企业和个人 用户规模 集团内20企业 案例背景 招商局集团深入落实“促进数据高效流通使用、赋能实体经济”精神,深化集团数字化水平&#xff0c…

2025年移动端开发性能优化实践与趋势分析

启动速度优化 本质:缩短首次可见帧渲染时间。 方法: iOS:利用Core ML本地模型轻量化部署,减少云端等待。Android:强制启用SplashScreen API,通过setKeepOnScreenCondition控制动画时长。冷启动需将耗时操…

Docker Compose介绍

基本概念 Docker-Compose是Docker官方的开源项目,负责实现对docker容器集群的快速编排。 可以这么理解,docker compose是docker提出的一个工具软件,可以管理多个docker容器组成一个应用,只需要编写一个YAML格式的配置文件docker…

头歌实践教学平台--【数据库概论】--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…

算法基础——模拟

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

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

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

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,另一 根是时钟…

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…

​​SenseGlove与Aeon Robotics携手推出HEART项目,助力机器人培训迈向新台阶

在自动化和机器人技术快速发展的今天,SenseGlove和Aeon Robotics联合推出了一项创新项目——HEART项目。该项目在欧盟资助的MasterXR框架内展开,旨在通过整合虚拟现实(VR)、力反馈触觉手套(SenseGlove项目Rembrandt&am…

mapbox进阶,仿照百度,加载marker点位,移入marker点切换图标,点击展示气泡,气泡和marker联动

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️marker 标注点位 api1.3.1 ☘️构造函数…

使用HTML5和CSS3实现3D旋转相册效果

使用HTML5和CSS3实现3D旋转相册效果 这里写目录标题 使用HTML5和CSS3实现3D旋转相册效果项目介绍技术栈核心功能实现思路1. HTML结构2. CSS样式解析2.1 基础样式设置2.2 3D效果核心样式2.3 卡片样式 3. JavaScript交互实现3.1 旋转控制3.2 自动播放功能 技术要点总结项目亮点总…

游戏引擎学习第186天

回顾并规划今天的任务 现在,我们站在了一个关键的时刻,准备突破,拥有一些优秀的性能分析代码。从目前来看,我们已经能够看到时间的消耗情况,我对这一点感到非常兴奋。昨天的直播中我们勉强让一些东西工作了&#xff0…