上下文管理器

一、自定义上下文管理器

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()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中,上下文管理器是一种处理资源的方式,它能够自动分配并且释放资源,比如打开关闭文件、数据库连接等。上下文管理器通常使用with语句来实现,保证了代码块执行完毕后,资源被释放。Python提供了两种实现上下文管理器的方式:使用类和使用装饰器。 使用类实现上下文管理器时,需要定义一个类,并且实现`__enter__()`和`__exit__()`方法。`__enter__()`方法会在进入代码块前被调用,它负责资源的分配;`__exit__()`方法则会在代码块执行完成后被调用,负责资源的释放。 以下是一个简单的示例: ```python class MyContext: def __init__(self, resource): self.resource = resource def __enter__(self): print("Entering context...") return self.resource def __exit__(self, exc_type, exc_val, exc_tb): print("Exiting context...") self.resource.close() with MyContext(open("file.txt", "w")) as f: f.write("Hello World!") ``` 使用装饰器实现上下文管理器时,需要定义一个函数,并且使用`@contextlib.contextmanager`装饰器修饰它。这个函数需要使用`yield`语句将控制权传递给with语句块,然后在finally块中释放资源。 以下是一个简单的示例: ```python from contextlib import contextmanager @contextmanager def my_context(resource): try: print("Entering context...") yield resource finally: print("Exiting context...") resource.close() with my_context(open("file.txt", "w")) as f: f.write("Hello World!") ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值