0基础跟德姆(dom)一起学AI Python进阶07-多线程_生成器

* 多进程案例

  * 带参数的多进程代码**(重点)**
  * 查看进程的id
  * 演示: 进程之间数据是相互隔离的

* 多线程案例

  * 入门案例
  * 带参数的多线程代码**(重点)**
  * 演示: 线程之间数据是相互共享的
  * 互斥锁

* 上下文管理器**(重点)**

  > 解析: with open原理, 为啥会自动释放资源.

* 生成器**(重点)**

  * 格式
  * yield关键字
  * 案例: 批量获取歌词

---

1.带参数的多进程代码

```python
"""
多进程解释:
    让多个任务同时执行.

思路:
    1. 导包.
    2. 创建进程类对象, 关联: 要执行的函数.
    3. 开启进程.

案例: 演示带参数的多进程代码, 要求大家掌握进程的参数.
    target: 关联的是目标函数名, 即: 该进程要执行的目标函数对象.
    name:   表示当前进程的名字, 默认是: Process-1, Process-2, Process-n... 可以修改, 一般不做.
    args:   表示以元组的形式传参, 实参和形参的个数, 顺序都要保持一致.
    kwargs: 表示以字典的形式传参, 实参和形参的个数, 参数名都要保持一致.
"""

# 0. 导包
import multiprocessing, time


# 1. 定义函数, 表示: 敲代码.
def coding(name, num):
    for i in range(num):
        print(f'{name} 正在敲第 {i} 遍代码...')
        time.sleep(0.1)

# 2. 定义函数, 表示: 听音乐.
def music(name, count):
    for i in range(count):
        print(f'{name} 正在听第 {i} 首音乐...')
        time.sleep(0.1)


# 3. 测试代码
if __name__ == '__main__':
    # 多线程(多进程): 代码演示.
    # 4. 创建进程类对象, 关联: 要执行的函数.
    # args参数介绍: 传参时, 实参和形参的个数, 包括顺序都必须保持一致.
    p1 = multiprocessing.Process(target=coding, args=('乔峰', 10), name='进程1_QQ')                    # 创建进程1, 关联: coding函数
    print(f'进程p1的名字为: {p1.name}')
    # kwargs参数介绍: 传参时, 实参和形参的个数, 参数名 都必须保持一致.
    p2 = multiprocessing.Process(target=music, kwargs={'count': 5, 'name':'虚竹'}, name='进程2_微信')    # 创建进程2, 关联: music函数
    print(f'进程p2的名字为: {p2.name}')

    # 5. 开启进程
    p1.start()
    p2.start()
```

2.多进程-获取进程id

```python
"""
案例: 演示如何 获取进程id

目的/作用:
    1. 在Python中, 1个进程 有1个 唯一的 进程号.  进程号是可以重复使用的(即: 进程结束, 进程号也会被释放).
    2. 知道了进程id, 方便我们管理进程(例如: 杀死指定进程, 查看指定进程的父进程)

格式:
    场景1: 获取当前进程的id
        方式1: os模块的 getpid()函数
        方式2: multiprocessing模块的 pid属性
    场景2: 获取当前进程的 父id
        os模块的 getppid()     # parent process, 父进程

"""

# 0. 导包
import multiprocessing, time, os


# 1. 定义函数, 表示: 敲代码.
def coding(name, num):
    for i in range(num):
        print(f'{name} 正在敲第 {i} 遍代码...')
        time.sleep(0.1)
        # 获取当前进程的pid, 以及父进程的id
        print(f'当前进程p1的pid为: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程的pid为: {os.getppid()}')

# 2. 定义函数, 表示: 听音乐.
def music(name, count):
    for i in range(count):
        print(f'{name} 正在听第 {i} 首音乐...')
        time.sleep(0.1)
        print(f'当前进程p2的pid为: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程的pid为: {os.getppid()}')


# 3. 测试代码
if __name__ == '__main__':
    # 多线程(多进程): 代码演示.
    # 4. 创建进程类对象, 关联: 要执行的函数.
    # args参数介绍: 传参时, 实参和形参的个数, 包括顺序都必须保持一致.
    p1 = multiprocessing.Process(target=coding, args=('乔峰', 10), name='进程1_QQ')                    # 创建进程1, 关联: coding函数
    print(f'进程p1的名字为: {p1.name}')
    # kwargs参数介绍: 传参时, 实参和形参的个数, 参数名 都必须保持一致.
    p2 = multiprocessing.Process(target=music, kwargs={'count': 5, 'name':'虚竹'}, name='进程2_微信')    # 创建进程2, 关联: music函数
    print(f'进程p2的名字为: {p2.name}')

    # 5. 开启进程
    p1.start()
    p2.start()

    # time.sleep(5)
    # 6. 打印main进程的id
    print(f'main进程的id为: {os.getpid()}, {multiprocessing.current_process().pid}')
```

3.进程间-数据隔离

```python
"""
案例: 演示进程之间 数据是相互隔离的.

大白话解释:
    1. 微信 和 QQ都是进程, 它们之间的数据都是 相互隔离的. 即: 微信不能直接访问QQ的数据, QQ也不能直接访问微信的数据.
    2. 下述代码你会发现, 多个子进程相当于把 父进程的资源全部拷贝了一份(注意: main外资源), 即: 子进程相当于父进程的副本.

需求: 定义全局变量 my_list = [], 搞两个进程, 分别实现往里边添加数据, 从里边读取数据, 并观察结果.
"""

# 导包
import multiprocessing, time

# 1. 定义全局变量.
my_list = []

# 2. 定义函数 write_data(), 往: 列表中 添加数据.
def write_data():
    # 为了让效果更明显, 我们添加多个值.
    for i in range(1, 6):
        my_list.append(i)       # 具体添加元素到列表的动作.
        print(f'add: {i}')      # 打印添加细节(过程)
    print(f'write_data函数: {my_list}')       # 添加完毕后, 打印列表结果


# 3. 定义函数 read_data(), 从: 列表中 读取数据.
def read_data():
    # 休眠一会儿, 确保 write_data()函数执行完毕.
    time.sleep(3)
    print(f'read_data函数: {my_list}')       # 打印列表结果


# 4. 在main中测试
if __name__ == '__main__':
    # 4.1 创建两个进程对象.
    p1 = multiprocessing.Process(target=write_data)     # 进程1, 例如: QQ
    p2 = multiprocessing.Process(target=read_data)      # 进程2, 例如: 微信

    # 4.2 开启进程.
    p1.start()
    p2.start()

    # print('看看我打印了几遍? ')   # main函数内的内容, 只执行一次.

print('看看我打印了几遍? ')   # main外资源, 自身要执行一次,  p1进程拷贝1遍,  p2进程拷贝1遍 = 3次
```

4.默认-主进程会等待子进程执行结束

```python
"""
案例:
    演示 默认情况下, 主进程会等待子进程执行结束在结束.

需求:
    创建1个子进程, 子进程执行完需要3秒钟, 让主进程1秒就结束, 观察: 整个程序是立即结束, 还是会等待子进程结束再结束.

结论:
    1. 默认情况下, 主进程会等待所有子进程执行结束再结束.
    2. 问: 如何实现, 当主进程结束的时候, 子进程也立即结束呢?
        方式1: 设置子进程为守护进程.   类似于: 公主 和 守护骑士.
        方式2: 手动关闭子进程.

"""

# 导包
import multiprocessing, time


# 1. 定义函数work(), 要被: 子进程关联.
def work():
    for i in range(10):
        print(f'工作中... {i}')
        time.sleep(0.3)     # 总计休眠时间: 0.3 * 10 = 3秒


# 2. 在main函数中测试.
if __name__ == '__main__':
    # 3. 创建子进程.
    p1 = multiprocessing.Process(target=work)
    # 4. 开启子进程.
    p1.start()

    # 5. 让主进程(main进程)休眠1秒.
    time.sleep(1)
    # 6. 主进程结束, 打印提示语句.
    print('主进程执行结束了!')
```

5.主进程关闭-子进程同步关闭

```python
"""
案例:
    演示 默认情况下, 主进程会等待子进程执行结束在结束.

需求:
    创建1个子进程, 子进程执行完需要3秒钟, 让主进程1秒就结束, 观察: 整个程序是立即结束, 还是会等待子进程结束再结束.

结论:
    1. 默认情况下, 主进程会等待所有子进程执行结束再结束.
    2. 问: 如何实现, 当主进程结束的时候, 子进程也立即结束呢?
        方式1: 设置子进程为守护进程.   类似于: 公主 和 守护骑士.
        方式2: 手动关闭子进程.

细节:
    1. 当非守护进程结束的时候, 它的所有守护进程都会立即终止, 且释放资源.
    2. 如果是 terminate()方式, 子进程主动结束自己, 则会变成僵尸进程, 不会立即释放资源.

"""

# 导包
import multiprocessing, time


# 1. 定义函数work(), 要被: 子进程关联.
def work():
    for i in range(10):
        print(f'工作中... {i}')
        time.sleep(0.3)     # 总计休眠时间: 0.3 * 10 = 3秒


# 2. 在main函数中测试.
if __name__ == '__main__':
    # 3. 创建子进程.
    p1 = multiprocessing.Process(target=work)

    # 核心细节: 设置p1为守护线程, 因为p1的父进程是main, 所以p1是main的守护进程.
    # p1.daemon = True

    # 4. 开启子进程.
    p1.start()

    # 5. 让主进程(main进程)休眠1秒.
    time.sleep(1)

    # 方式2: 在主进程执行结束前, 子进程主动自己关闭自己.
    p1.terminate()  # 不推荐用, 会变成僵尸进程, 不会主动释放资源, 过段时间会交由(init进程, 充当父进程),来释放资源.

    # 6. 主进程结束, 打印提示语句.
    print('主进程执行结束了!')
```

 6.多线程-入门

```python
"""
多任务解释:
    概述:
        多个任务同时执行.
    目的:
        提高资源利用率, 提高效率.
    实现方式:
        方式1: 多进程.
        方式2: 多线程.

    回顾, 之前讲解的概述:
        进程:
            CPU分配资源的基本单位, 例如: 可执行程序, *.exe
            可以理解为: 车.
        线程:
            CPU调度资源的最小单位, 即: 进程的执行单元, 执行路径.
            可以理解为: 车道
        多进程:
            多个*.exe, 例如: 微信.exe, QQ.exe, 即: 不同的软件.
        多线程:
            同一个进程, 有多条执行路径, 例如: 微信.exe -> 查看钱包, 和张三聊天, 和李四聊天, 查看照片...

需求: 用多线程思路, 实现: 一边写代码, 一边听音乐.
"""

# 导包
import threading, time

# 1. 定义函数coding(), 表示: 敲代码
def coding():
    for i in range(10):
        print(f'正在敲第 {i} 遍代码! -------')
        time.sleep(0.1)

# 2. 定义函数music(), 表示: 听音乐.
def music():
    for i in range(10):
        print(f'正在听第 {i} 首音乐!')
        time.sleep(0.1)

# 3. 在main中测试.
if __name__ == '__main__':
    # 4. 创建两个线程对象.
    t1 = threading.Thread(target=coding)
    t2 = threading.Thread(target=music)

    # 5. 开启线程.
    t1.start()
    t2.start()
```

7.多线程-带参数

```python
"""
多任务解释:
    概述:
        多个任务同时执行.
    目的:
        提高资源利用率, 提高效率.
    实现方式:
        方式1: 多进程.
        方式2: 多线程.

    回顾, 之前讲解的概述:
        进程:
            CPU分配资源的基本单位, 例如: 可执行程序, *.exe
            可以理解为: 车.
        线程:
            CPU调度资源的最小单位, 即: 进程的执行单元, 执行路径.
            可以理解为: 车道
        多进程:
            多个*.exe, 例如: 微信.exe, QQ.exe, 即: 不同的软件.
        多线程:
            同一个进程, 有多条执行路径, 例如: 微信.exe -> 查看钱包, 和张三聊天, 和李四聊天, 查看照片...

需求: 用多线程思路, 实现: 一边写代码, 一边听音乐.  带参数版.
"""

# 导包
import threading, time

# 1. 定义函数coding(), 表示: 敲代码
def coding(name, num):
    for i in range(num):
        print(f'{name} 正在敲第 {i} 遍代码! -------')
        time.sleep(0.1)

# 2. 定义函数music(), 表示: 听音乐.
def music(name, count):
    for i in range(count):
        print(f'{name} 正在听第 {i} 首音乐!', end='\n')
        time.sleep(0.1)

# 3. 在main中测试.
if __name__ == '__main__':
    # 4. 创建两个线程对象.
    # Thread()类中的参数如下:
    #   target      关联要执行的目标函数对象.
    #   name        设置线程名
    #   args        以元组形式传参, 个数, 顺序都要保持一致.
    #  kwargs       以字典形式传参, 个数, 参数名(键名和形参名)保持一致.
    t1 = threading.Thread(target=coding , args = ('乔峰', 9))
    t2 = threading.Thread(target=music, kwargs={'name': '虚竹', 'count': 10})

    # 5. 开启线程.
    t1.start()
    t2.start()
```

8.多线程-执行顺序

```python
"""
案例:
    演示多线程代码 的执行顺序.

结论:
    1. 多线程的执行具有 随机性, 因为: 线程是由CPU调度的.
    2. 调度资源常用的两种方式:
        抢占式调度: 谁抢到资源, 谁执行.
            Python, Java用的都是这种思路.
        均分时间片: 每个任务获取CPU的时间都是固定的.
"""

# 导包
import threading
import time


# 1. 定义函数get_info(), 用来打印: 线程信息, 看看当前是哪个线程在执行.
def get_info():
    # 休眠一会儿, 让效果更明显.
    time.sleep(0.1)

    # 1.1 获取当前的线程对象.
    ct = threading.current_thread()

    # 1.2 打印当前线程对象即可.
    print(f'当前的线程对象是: {ct}')


# 2. 在main中测试.
if __name__ == '__main__':
    # 3. 为了让效果更明显, 创建多个线程.
    # t1 = threading.Thread(target=get_info)
    # t2 = threading.Thread(target=get_info)
    #
    # t1.start()
    # t2.start()

    for i in range(10):
        t1 = threading.Thread(target=get_info)
        t1.start()
```

9.多线程-等待子线程结束再结束

```python
"""
案例:
    演示 默认情况下, 主线程会等待子线程执行结束在结束.

需求:
    创建1个子线程, 子线程执行完需要3秒钟, 让主线程1秒就结束, 观察: 整个程序是立即结束, 还是会等待子线程结束再结束.

结论:
    1. 默认情况下, 主线程会等待所有子线程执行结束再结束.
    2. 问: 如何实现, 当主线程结束的时候, 子线程也立即结束呢?
        设置子线程为守护线程.   类似于: 公主 和 守护骑士.

"""

# 导包
import threading, time


# 1. 定义函数work(), 要被: 子线程关联.
def work():
    for i in range(10):
        print(f'工作中... {i}')
        time.sleep(0.3)     # 总计休眠时间: 0.3 * 10 = 3秒


# 2. 在main函数中测试.
if __name__ == '__main__':
    # 3. 创建子线程.
    t1 = threading.Thread(target=work)
    # 4. 开启子线程.
    t1.start()

    # 5. 让主线程(main线程)休眠1秒.
    time.sleep(1)
    # 6. 主线程结束, 打印提示语句.
    print('主线程执行结束了!')
```

10.多线程-守护线程

```python
"""
案例:
    演示 默认情况下, 主线程会等待子线程执行结束在结束.

需求:
    创建1个子线程, 子线程执行完需要3秒钟, 让主线程1秒就结束, 观察: 整个程序是立即结束, 还是会等待子线程结束再结束.

结论:
    1. 默认情况下, 主线程会等待所有子线程执行结束再结束.
    2. 问: 如何实现, 当主线程结束的时候, 子线程也立即结束呢?
        设置子线程为守护线程.   类似于: 公主 和 守护骑士.

结论:
    当非守护线程结束的时候, 和它关联的守护线程, 也会立即结束, 释放资源.
"""

# 导包
import threading, time


# 1. 定义函数work(), 要被: 子线程关联.
def work():
    for i in range(10):
        print(f'工作中... {i}')
        time.sleep(0.3)  # 总计休眠时间: 0.3 * 10 = 3秒


# 2. 在main函数中测试.
if __name__ == '__main__':
    # 3. 创建子线程.
    # 守护线程, 格式1: 创建线程对象的时候, 直接指定.
    # t1 = threading.Thread(target=work, daemon=True)

    t1 = threading.Thread(target=work)
    # 守护线程, 格式2: 创建线程对象后, 通过 daemon属性设置.
    # t1.daemon = True

    # 守护线程, 格式3: 创建线程对象后, 通过 setDaemon()函数设置.
    t1.setDaemon(True)      # 类似于以前我们学的: get_name(), set_name()

    # 4. 开启子线程.
    t1.start()

    # 5. 让主线程(main线程)休眠1秒.
    time.sleep(1)

    # 6. 主线程结束, 打印提示语句.
    print('主线程执行结束了!')
```

11.多线程-共享资源

```python
"""
案例: 演示 同一进程的多个线程, 可以共享 该进程的资源.

结论:
    1. 进程之间, 数据相互隔离.        进程 = 软件.
    2. 线程之间, 数据共享.
"""

# 导包
import threading, time

# 1. 定义全局变量.
my_list = []


# 2. 定义函数write_data(), 实现: 往列表中添加数据.
def write_data():
    # 为了效果更明显, 我们添加多个元素.
    for i in range(1, 6):
        # time.sleep(0.1)
        # 往列表中添加元素.
        my_list.append(i)
        # 打印添加的细节
        print(f'add: {i}')

    # 添加之后, 打印结果.
    print(f'write_data函数: {my_list}')


# 3. 定义函数read_data(), 实现: 从列表中读取数据.
def read_data():
    time.sleep(2)     # 为了效果更好看, 加入休眠线程.
    print(f'read_data函数: {my_list}')


# 4. main函数中进行测试.
if __name__ == '__main__':
    # 4.1 创建两个线程对象, 分别关联: write_data(), read_data()函数.
    t1 = threading.Thread(target=write_data)
    t2 = threading.Thread(target=read_data)

    # 4.2 启动线程.
    t1.start()
    t2.start()
```

12.多线程-操作共享资源-非法值

```python
"""
案例:  线程可以共享全局变量, 同时操作, 可能会出现: 安全问题.

遇到的问题:
    两个线程同时操作共享变量, 进行累加操作, 出现: 累计次数"不够"的情况, 我们要的效果是:  get_sum1: 100W,  get_sum2:200W, 但是结果不是.
产生原因:
    正常情况:
        假设g_num = 0
            第1次循环:  t1线程抢到资源, 对其累加1, 操作后 g_num = 1, t1线程本次操作完毕.
            第2次循环: 假设t2线程抢到了资源, 此时 g_num = 1, 操作之后(累加1), g_num = 2, t2线程本次操作完毕.
        这个是正常情况.

    非正常情况:
        1. 假设 g_num = 0
        2. 假设 t1 线程抢到了资源, 在还没有来得及对 g_num(全局变量)做 累加操作的时候, 被t2线程抢走了资源.
        3. t2线程也会去读取 g_num(全局变量)的值, 此时 g_num = 0
        4. 之后t1 和 t2线程分别开始了 累加操作:
            t1线程, 累加之后,   g_num = 0     ->    g_num = 1
            t2线程, 累加之后,   g_num = 0     ->    g_num = 1
        5. 上述一共累加了 2次, 但是 g_num的值最终只 累加了 1.


"""
import threading

# 需求: 定义两个函数, 分别对全局变量累加100W次, 并打印观察程序的最终运行结果.

# 1. 定义全局变量.
g_num = 0

# 2. 定义函数 get_sum1(), 实现对全局变量累加 100W次
def get_sum1():
    for i in range(1000000):
        global g_num
        g_num += 1      # 全局变量 + 1
    # 程序计算完毕后, 打印结果.
    print(f'get_sum1函数计算结果: {g_num}')       # 100W

# 3. 定义函数 get_sum2(), 实现对全局变量累加 100W次
def get_sum2():
    for i in range(1000000):
        global g_num
        g_num += 1      # 全局变量 + 1
    # 程序计算完毕后, 打印结果.
    print(f'get_sum2函数计算结果: {g_num}')       # 200W

# 4. 测试
if __name__ == '__main__':
    # 4.1 创建两个线程对象, 分别关联: 两个函数.
    t1 = threading.Thread(target=get_sum1)
    t2 = threading.Thread(target=get_sum2)

    # 4.2 启动线程.
    t1.start()
    t2.start()
```

13.互斥锁

```python
"""
案例:  线程可以共享全局变量, 同时操作, 可能会出现: 安全问题.

遇到的问题:
    两个线程同时操作共享变量, 进行累加操作, 出现: 累计次数"不够"的情况, 我们要的效果是:  get_sum1: 100W,  get_sum2:200W, 但是结果不是.
产生原因:
    正常情况:
        假设g_num = 0
            第1次循环:  t1线程抢到资源, 对其累加1, 操作后 g_num = 1, t1线程本次操作完毕.
            第2次循环: 假设t2线程抢到了资源, 此时 g_num = 1, 操作之后(累加1), g_num = 2, t2线程本次操作完毕.
        这个是正常情况.

    非正常情况:
        1. 假设 g_num = 0
        2. 假设 t1 线程抢到了资源, 在还没有来得及对 g_num(全局变量)做 累加操作的时候, 被t2线程抢走了资源.
        3. t2线程也会去读取 g_num(全局变量)的值, 此时 g_num = 0
        4. 之后t1 和 t2线程分别开始了 累加操作:
            t1线程, 累加之后,   g_num = 0     ->    g_num = 1
            t2线程, 累加之后,   g_num = 0     ->    g_num = 1
        5. 上述一共累加了 2次, 但是 g_num的值最终只 累加了 1.

解决方案:
    采用 线程同步 解决.

线程同步解释:
    概述:
        线程同步, 也叫线程安全, 它是用来解决 多线程操作共享变量, 可能出现非法值的问题的.
    解决思路:
        加锁.
    步骤:
        1. 创建锁.
        2. 加锁.
        3. 释放锁.
    细节:
        1. 必须使用同一把锁, 否则可能出现锁不住的情况.
        2. 选择合适的时机要释放锁, 否则可能出现死锁的情况.
"""
import threading

# 需求: 定义两个函数, 分别对全局变量累加100W次, 并打印观察程序的最终运行结果.

# 1. 定义全局变量.
g_num = 0

# 核心细节, 创建: 锁(互斥锁)
mutex = threading.Lock()        # mutex是变量名, 符合规范即可. mutex单词 是 互斥的意思.
# mutex2 = threading.Lock()        # mutex是变量名, 符合规范即可. mutex单词 是 互斥的意思.

# 2. 定义函数 get_sum1(), 实现对全局变量累加 100W次
def get_sum1():

    # 加锁
    mutex.acquire()
    for i in range(1000000):
        global g_num
        g_num += 1      # 全局变量 + 1
    # 程序计算完毕后, 打印结果.
    # 解锁
    mutex.release()
    print(f'get_sum1函数计算结果: {g_num}')       # 100W

# 3. 定义函数 get_sum2(), 实现对全局变量累加 100W次
def get_sum2():
    # 加锁
    mutex.acquire()
    for i in range(1000000):
        global g_num
        g_num += 1      # 全局变量 + 1
    # 程序计算完毕后, 打印结果.
    # 解锁
    mutex.release()
    print(f'get_sum2函数计算结果: {g_num}')       # 200W

# 4. 测试
if __name__ == '__main__':
    # 4.1 创建两个线程对象, 分别关联: 两个函数.
    t1 = threading.Thread(target=get_sum1)
    t2 = threading.Thread(target=get_sum2)

    # 4.2 启动线程.
    t1.start()
    t2.start()

```

14.回顾open语法

```python
"""
回顾: open() 操作文件的语法.


"""

# 1. 简单的读数据的代码
# src_f = open('./1.txt', 'r', encoding='utf-8')
# print(src_f.read())
# src_f.close()


# 2. 上述代码存在两个问题.  问题1: 有异常(数据源文件可能不存在), 需要解决.   问题2: 可能会忘记 close()释放资源.
# 解决方案: 用 try.except.finally解决.
# try:
#     src_f = open('./1.txt', 'r', encoding='utf-8')
#     print(src_f.read())
# except:
#     print('哎呀, 程序出问题了!')
# finally:
#     # src_f.close()     # 有可能忘记 关闭资源.
#     pass


# 3. 上述代码虽然解决了异常问题, 但是出现了新的问题:  问题1: 上述代码冗余, 阅读性相对较差.    问题2: 可能会忘记 close()释放资源.
# 解决方案: with...open()...  语句
with open('./1.txt', 'r', encoding='utf-8') as src_f:
    print(src_f.read())     # with.oepn()会等待其内部代码执行完毕后, 自动释放资源.


# 4. 为啥with.open()会自动释放资源呢? 我们很好奇呀, 所以我们来探究一下它的底层原理, 其实就是: 上下文管理器对象.
```

15.自定义上下文管理器

```python
"""
案例: 演示自定义上下文管理器.

细节:
    1. with ... as ...  会在其内容执行完毕后, 自动使用.
    2. with ... as ... 功能之所以强大, 背后就是靠 上下文管理器来支撑的.
    3. 一个类, 只要重写了 __enter__(),  __exit__() 这两个函数, 它就是 上下文管理器.

特点:
    1. __enter__() 会在 with语句之前 自动执行.
    2. __exit__() 会在 with语句之后 自动执行.
"""

# 需求: 自定义一个 MyFile类, 实现读写文件的操作.
# 1. 创建自定义类 MyFile
class MyFile(object):
    # 2. 重写 __init__()函数, 指定: 初始化的属性.
    def __init__(self, file_name, file_model):
        self.file_name = file_name      # 要被操作的: 文件名
        self.file_model = file_model    # 要进行操作的: 模式, r, rb, w, wb...
        self.fp = None                  # file path = open(....)  就是1个文件对象.

    # 3. 重写 __enter__(), 它会在with之前自动执行.
    def __enter__(self):
        # 3.1 获取具体的文件对象, 赋值给 fp对象.
        self.fp = open(self.file_name, self.file_model, encoding='utf-8')
        # 3.2 返回 文件对象.
        # return self.fp      # fp 就是 文件对象.

        return self     # 实际开发中, 更推荐直接返回 文件对象(MyFile)


    # 4. 重写 __exit__(), 它会在with之后自动执行, 一般: 释放资源.
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 4.1 具体的释放资源的动作.
        self.fp.close()

        # 4.2 打印提示语句, 告知: 释放资源了.
        print('释放资源成功!')


# 5. 在main函数中测试调用.
if __name__ == '__main__':
    # with open('./1.txt', 'r', encoding='utf-8') as src_f:

    # 如果 MyFile类, 返回的是: self.fp, 即: open() 文件对象.  写法如下.
    # with MyFile('./1.txt', 'r') as fp:
    #     print(fp.read())  # with.oepn()会等待其内部代码执行完毕后, 自动释放资源.


    # 如果MyFile类, 返回的是: MyFile(), 我们自定义的文件对象, 写法如下:
    with MyFile('./1.txt', 'r') as mf:      # mf = MyFile()对象
        print(mf.fp.read())  # with.oepn()会等待其内部代码执行完毕后, 自动释放资源.
```

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity6 + UE5.4 PSO缓存实践记录

题图(取自COD冷战的着色器编译提示) PSO(管线状态对象 Pipeline State Object)是伴随现代图形API(DirectX12、Vulkan、Metal)而出现的概念,它本质上是单次绘制时渲染管线所处的状态信息的集合&…

机器学习中的聚类艺术:探索数据的隐秘之美

一 什么是聚类 聚类是一种经典的无监督学习方法,无监督学习的目标是通过对无标记训练样本的学习,发掘和揭示数据集本身潜在的结构与规律,即不依赖于训练数据集的类标记信息。聚类则是试图将数据集的样本划分为若干个互不相交的类簇&#xff…

关于武汉高芯coin417G2红外机芯的二次开发

文章目录 前言一、外观和机芯参数二、SDK的使用1、打开相机2、回调函数中获取全局温度和图像3、关闭相机 前言 最近工作中接触了一款基于武汉高芯科技有限公司开发的红外模组,即coin417g2(测温型)9.1mm镜头.使用此模组,开发了一套红外热成像检测桌面应用程序.下面简单记录下该…

PHP轻量级高性能HTTP服务框架 - webman

摘要 webman 是一款基于 workerman 开发的高性能 HTTP 服务框架。webman 用于替代传统的 php-fpm 架构,提供超高性能可扩展的 HTTP 服务。你可以用 webman 开发网站,也可以开发 HTTP 接口或者微服务。 除此之外,webman 还支持自定义进程&am…

UE5 C++ 读取图片插件(一)

原来UE可以使用 static,之前不知道&#xff0c;一用就报错。 static TSharedPtr<IImageWrapper> GetImageWrapperByExtention(const FString InImagePath); //智能指针&#xff0c;方便追寻引用C,加载ImageWrapperstatic UTexture2D* LoadTexture2D(const FString& …

大路灯护眼灯有必要吗安全吗?性价比高落地护眼灯推荐

大路灯护眼灯有必要吗安全吗&#xff1f;近几年来&#xff0c;随着生活节奏的加快&#xff0c;目前青少年的近视率呈现一个直线上升的趋势&#xff0c;其中占比达到了70%以上&#xff0c;并且最令人意外的是小学生竟然也占着比较大的比重&#xff0c;这一系列的数据不仅表明着近…

Kafka【五】Buffer Cache (缓冲区缓存)、Page Cache (页缓存)和零拷贝技术

【1】Buffer Cache (缓冲区缓存) 在Linux操作系统中&#xff0c;Buffer Cache&#xff08;缓冲区缓存&#xff09;是内核用来优化对块设备&#xff08;如磁盘&#xff09;读写操作的一种机制&#xff08;故而有一种说法叫做块缓存&#xff09;。尽管在较新的Linux内核版本中&a…

联众优车持续加大汽车金融服务投入与创新,赋能汽车消费新生态

近年来&#xff0c;中国汽车消费市场呈现出蓬勃发展的态势&#xff0c;而汽车金融服务作为降低购车门槛、优化购车体验的重要手段&#xff0c;正日益受到市场的青睐。《2023中国汽车消费趋势调查报告》显示&#xff0c;相较于前一年&#xff0c;今年选择汽车金融服务的市场消费…

实战docker第二天——cuda11.8,pytorch基础环境docker打包

在容器化环境中打包CUDA和PyTorch基础环境&#xff0c;可以将所有相关的软件依赖和配置封装在一个Docker镜像中。这种方法确保了在不同环境中运行应用程序时的一致性和可移植性&#xff1a; Docker&#xff1a;提供了容器化技术&#xff0c;通过将应用程序及其所有依赖打包在一…

天然药物化学史话:甾体化合物-文献精读45(甾体化合物化学历史综述-地表最强综述系列-45)

天然药物化学史话:甾体化合物&#xff0c;极好的一篇综述&#xff0c;地表最强综述系列-45 摘要 甾体化合物是一类重要的天然产物,在人类发展史上不仅为人类的健康做出了特殊的贡献,而且其立体结构的特殊性方面也在有机化学发展史,特别是有机化学理论上占有极其重要的地位,完善…

如何为 DigitalOcean 静态路由操作员设置故障转移

静态路由操作器的主要目的是提供更大的灵活性&#xff0c;并在 Kubernetes 环境中控制网络流量。它使你能够根据应用程序的需求自定义路由配置&#xff0c;从而优化网络性能。该操作器作为 DaemonSet 部署&#xff0c;因此将在你的 DigitalOcean Managed Kubernetes 集群的每个…

6.2高斯滤波

目录 实验原理 示例代码1 运行结果1 示例代码2 运行结果2 实验代码3 运行结果3 实验原理 在OpenCV中&#xff0c;高斯滤波&#xff08;Gaussian Filtering&#xff09;是一种非常常用的图像平滑处理方法。它通过使用一个高斯核&#xff08;即高斯分布函数&#xff09;对…

南通网站建设手机版网页

随着移动互联网的迅猛发展&#xff0c;越来越多的人通过手机浏览网页&#xff0c;进行在线购物、信息查询和社交互动。因此&#xff0c;建立一个适合移动端访问的网站已成为企业和个人不可忽视的重要任务。在南通&#xff0c;网站建设手机版网页的需求逐渐增加&#xff0c;如何…

单体到微服务:架构变迁

单体架构与微服务架构&#xff1a;从单体到微服务的演变 引言单体架构概述微服务架构的优势一、功能定位二、使用场景三、配置方式四、性能特点Eureka - 服务注册与发现框架核心功能工作原理优势应用场景 结论 引言 在软件开发的世界中&#xff0c;随着业务的增长和技术的发展…

艾体宝洞察丨透过语义缓存,实现更快、更智能的LLM应用程序

传统的缓存只存储数据而不考虑上下文&#xff0c;语义缓存则不同&#xff0c;它能理解用户查询背后的含义。它使数据访问更快&#xff0c;系统响应更智能&#xff0c;对 GenAI 应用程序至关重要。 什么是语义缓存&#xff1f; 语义缓存解释并存储用户查询的语义&#xff0c;使…

MQTT broker搭建并用SSL加密

系统为centos&#xff0c;基于emqx搭建broker&#xff0c;流程参考官方。 安装好后&#xff0c;用ssl加密。 进入/etc/emqx/certs,可以看到 分别为 cacert.pem CA 文件cert.pem 服务端证书key.pem 服务端keyclient-cert.pem 客户端证书client-key.pem 客户端key 编辑emqx配…

云计算之大数据(下)

目录 一、Hologres 1.1 产品定义 1.2 产品架构 1.3 Hologres基本概念 1.4 最佳实践 - Hologres分区表 1.5 最佳实践 - 分区字段设置 1.6 最佳实践 - 设置字段类型 1.7 最佳实践 - 存储属性设置 1.8 最佳实践 - 分布键设置 1.9 最佳实践 - 聚簇键设置 1.10 最佳实践 -…

12. GIS地图制图工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

多线程 | ThreadLocal源码分析

文章目录 1. ThreadLocal解决了什么问题数据隔离避免参数传递资源管理 2. ThreadLocal和Synchronized3. ThreadLocal核心核心特性常见方法使用场景注意事项 4. ThreadLocal如何实现线程隔离的&#xff1f;&#xff08;重点&#xff09;ThreadLocal 的自动清理与内存泄漏问题阿里…

浙大数据结构:02-线性结构3 Reversing Linked List

数据结构MOOC PTA习题 这道题也是相当费事&#xff0c;不过比上一个题好一些&#xff0c;这里我使用了C的STL库&#xff0c;使得代码量大幅减少。 题干机翻&#xff1a; 1、条件准备 这里我准备采用map来存地址和值&#xff0c;因为map的查找效率也是不错的 数组arr是存链…