以下是整理后的笔记格式:
---
# 闭包与装饰器
## 一、闭包的概念,作用,条件### 作用:
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`,每次调用这些函数时都会触发登录验证流程。只有当输入正确的用户名和密码后,才会执行相应的函数操作。
这段代码通过装饰器实现了一个简单的登录验证机制,确保在执行特定操作前用户已经通过验证。