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

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

---

# 闭包与装饰器

## 一、闭包的概念,作用,条件### 作用:
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`,每次调用这些函数时都会触发登录验证流程。只有当输入正确的用户名和密码后,才会执行相应的函数操作。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值