上下文管理器

本文详细介绍了Python的上下文管理协议,包括上下文管理器的概念、自定义上下文管理器的实现,以及如何捕获异常。示例中展示了如何自定义文件读写上下文管理器,并演示了在遇到异常时如何处理。此外,还探讨了基于生成器的上下文管理器的使用。最后,列举了上下文管理器在数据库连接、资源管理等场景的应用,强调了其在确保资源正确释放和异常处理方面的优势。
摘要由CSDN通过智能技术生成

一、自定义上下文管理器

1.1 基本概念

**上下文管理协议 (Context management protocol):**包含 __enter__() 和 __exit__() 方法。

**上下文管理器 (Context manager):**实现 __enter__() 和 __exit__() 方法的对象。上下文管理器用于 定义执行 with 语句时要建立的运行时上下文,并负责执行 with 语句块 上下文的进入与退出 操作。通常使用 with 语句调用上下文管理器,也可通过直接调用其方法来使用。

**运行时上下文 (Runtime context):**由上下文管理器创建通过__enter__() 和 __exit__() 方法方法实现进入和退出。其中,__enter__() 方法在语句体执行开始前进入运行时上下文, __exit__() 在语句体执行结束后退出运行时上下文。with 语句支持“运行时上下文”这一概念。
**上下文表达式 (Context expression):**with 语句中跟在关键字 with 后的表达式。该表达式返回一个上下文管理器对象。

**语句体 (with-body):**即 with 语句中的代码块。执行语句体前,会调用上下文管理器的 __enter__() 方法;执行语句体后,会调用上下文管理器 __exit__() 方法。

# with语句的语法格式
with EXPR as VAR:
    with-body

1.2 自定义文件读写上下文管理器

# -*- coding: utf-8 -*-
# @Time     : 2021/11/27 12:39
# @Author   : wljess
# @File     : 上下文管理器.py
# @Describe : 
# @Software : PyCharm

class Context:

    def __init__(self, name, mode):
        self.name = name
        self.mode = mode
        # 文件句柄,资源对象
        self.file_handle = None

    def __enter__(self):
        # 进入上下文管理器的运行时上下文,获取资源
        print("__enter__")
        self.file_handle = open("info.txt", "w")
        return self.file_handle

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 退出上下文管理器的运行时上下文,释放资源
        print("__exit__")
        self.file_handle.close()


with Context("info.log", 'w') as f:
    print("业务逻辑主体部分")
    f.write("hello world!!!")

'''
上下文管理器调用解析:
1. with语句先暂存Context类的__exit__方法,调用Context类的 __enter__ 方法 (进入上下文管理器的运行时上下文)。 
2. __enter__方法打开文件info.log文件并返回给with语句,而打开的文件句柄被传递给变量f。
3.执行with语句体/代码块,令f调用write()方法在文件info.log中写入"hello world!!!" 。
4.with语句调用先前暂存的__exit__方法 (退出上下文管理器的运行时上下文),正常关闭文件info.log文件。
'''

1.3 上下文管理器捕获异常

# -*- coding: utf-8 -*-
# @Time     : 2021/11/27 15:11
# @Author   : wljess
# @File     : 上下文管理器捕获异常.py
# @Describe : 
# @Software : PyCharm

class Context:

    def __init__(self, name, mode):
        self.name = name
        self.mode = mode
        # 文件句柄,资源对象
        self.file_handle = None

    def __enter__(self):
        # 进入上下文管理器的运行时上下文,获取资源
        print("__enter__")
        self.file_handle = open(self.name, self.mode)
        return self.file_handle

    def __exit__(self, exc_type, exc_val, exc_tb):
        '''
        :param exc_type: 异常类型
        :param exc_val: 异常值
        :param exc_tb: 异常回溯追踪
        :return:
        '''
        # 退出上下文管理器的运行时上下文,释放资源
        print("__exit__")
        if exc_val:
            print(exc_type)
            print(exc_tb)
            print(exc_val)

        self.file_handle.close()

        return True


with Context("info.txt", 'r') as f:
    print("业务逻辑主体部分")
    # 调用不存在的undefinded_method方法时,抛出了异常
    f.undefinded_method("Bye")

'''
__exit__()方法中捕捉的异常,实际上是with语句体(with代码块)所抛出的异常,默认__exit__()方法返回值为None,
若__exit__()方法返回 True,则说明该异常已被“处理”,无需再处理
若__exit__()方法返回 True 之外的任何内容,则该异常将被 with 语句抛出
'''

二、基于生成器的上下文管理器

# -*- coding: utf-8 -*-
# @Time     : 2021/11/27 16:08
# @Author   : wljess
# @File     : 基于生成器的上下文管理器.py
# @Describe : 
# @Software : PyCharm

from contextlib import contextmanager


@contextmanager
def file_manager(name, mode):
    try:
        f = open(name, mode)
        yield f
    except Exception as error:
        print(type(error))
        print(error)
    finally:
        f.close()

with file_manager("info.txt", "r") as file:
    content = file.read()
    print(content)

三、上下文管理器应用场景

​ 上下文管理器的典型用途包括:保存和恢复各种全局状态、锁定和解锁资源、关闭已打开的文件 等。

# mysql数据库连接
# -*- coding: utf-8 -*-
# @Time     : 2021/11/27 17:00
# @Author   : wljess
# @File     : 上下文管理器实现mysql连接.py
# @Describe : 
# @Software : PyCharm

import pymysql


class DataBase:

    def __init__(self, host, username, pwd, db_name):
        self.coon = pymysql.connect(host=host, user=username, passwd=pwd, database=db_name)
        self.cursor = self.coon.cursor()

    def __enter__(self):

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.coon.close()
        self.cursor.close()

    def query(self, sql):
        self.cursor.execute(sql)
        result = self.cursor.fetchone()
        return result
    

with DataBase("localhost", "root", "", "mydb") as db:
    print(db.query("select name,age,salary from user_table where id=4947;"))

# 管理多个资源
# 我们要同时打开两个文件进行处理,不优雅的写法
with open("file1", "r") as f1:
    with open(file2, "w") as f2:
    	f2.write(f1.read())
# 优雅的写法
with open("file1", "r") as f1, open("file2", "w") as f2:
    f2.write(f1.read())
# -*- coding: utf-8 -*-
# @Time     : 2021/11/27 17:00
# @Author   : wljess
# @File     : 上下文管理器管理锁.py
# @Describe : 
# @Software : PyCharm

import time
import threading


class Singleton:
    """单例模式————最多只允许创建一个该类实例"""
    _lock = threading.Lock()
    _instance = None

    def __new__(cls, *args, **kwargs):
        time.sleep(1)   # 模拟高并发情况
        with cls._lock:
            if not cls._instance:
                cls._instance = object.__new__(cls)

        return cls._instance

def task(a, b):
    s = Singleton(a, b)
    print(s)


for i in range(5):
    t = threading.Thread(target=task, args=(2, 3))
    t.start()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值