首页 > 编程语言 >python-contextlib上下文管理器

python-contextlib上下文管理器

时间:2023-07-05 09:34:11浏览次数:53  
标签:__ 管理器 exc python value contextlib print 上下文

python contextlib上下文管理器

python-contextlib

  • 上下文管理器 两大作用:
    -- 可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接;
    -- 可以以一种更加优雅的方式,处理异常

读取文件的一般流程

# 打开文件
f = open('file.txt')
try:
    for line in f:
        # 读取文件内容 执行其他操作
        # do_something...
finally:
    # 保证关闭文件
    f.close()

在读取文件内容和操作期间,无论是否发生异常,都可以保证最后能释放文件资源。

使用with实现

with open('filename.csv') as file:
    print(file.readlines())
with context_expression [as target(s)]:
    with-body

with 语法非常简单,我们只需要 with 一个表达式,然后就可以执行自定义的业务逻辑。

类的内部实现

一个类在 Python 中,只要实现以下方法,就实现了「上下文管理器协议」:

  • __enter__:在进入 with 语法块之前调用,返回值会赋值给 withtarget
  • __exit__:在退出 with 语法块时调用,一般用作异常处理, 除了 self 之外,必须传入另外三个参数,分别表示 exception 的类型
class TestContext:

    def __enter__(self):
        print('__enter__')
        return 1

    def __exit__(self, exc_type, exc_value, exc_tb):
        print('exc_type: %s' % exc_type)
        print('exc_value: %s' % exc_value)
        print('exc_tb: %s' % exc_tb)

with TestContext() as t:
    print('t: %s' % t)
    
    
# Output:
# __enter__
# t: 1
# exc_type: None
# exc_value: None
# exc_tb: None

从输出结果我们可以看到,具体的执行流程如下:

  • __enter__ 在进入 with 语句块之前被调用,这个方法的返回值赋给了 with 后的 t 变量
  • __exit__ 在执行完 with 语句块之后被调用

如果在 with 语句块内发生了异常,那么 __exit__ 方法可以拿到关于异常的详细信息:

  • exc_type:异常类型
  • exc_value:异常对象
  • exc_tb:异常堆栈信息
class Sample():
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):

        if exc_type == IndexError:
            print("执行出错 IndexError")
            print(exc_value, type(exc_value))
            print(traceback)
            return True
        elif exc_type == ZeroDivisionError:
            print("执行出错 ZeroDivisionError")
            print(exc_value, type(exc_value))
            print(traceback)
            return True

    def do_wrong_job(self):
        a = 1 / 0
        # 1 / 0 ,是一个错误的式子,应该会报错
        return a


with Sample() as sample:
    sample.do_wrong_job()

    
# 执行结果
执行出错 ZeroDivisionError
division by zero <class 'ZeroDivisionError'>
<traceback object at 0x00000237CF4F59C0>

contextlib模块

对于需要上下文管理的场景,除了自己实现 __enter____exit__ 之外,还有更简单的方式,只写一个函数就可以实现上下文管理器

contextlib 是一个装饰器,只要按照它的代码协议来实现函数内容,就可以将这个函数对象变成一个上下文管理器。

__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext",
           "AbstractContextManager", "AbstractAsyncContextManager",
           "AsyncExitStack", "ContextDecorator", "ExitStack",
           "redirect_stdout", "redirect_stderr", "suppress"]

contextmanager装饰器

生成器函数转为上下文管理器

@contextmanagercontextlib 模块提供的一个装饰器,可以将一个生成器函数转换成一个上下文管理器

from contextlib import contextmanager

@contextmanager
def my_context():
    # 进入上下文前的操作
    print('entering context')
    try:
        yield  # 生成器函数的 `yield` 语句之前的代码作为上文,之后的代码作为下文
    finally:
        # 离开上下文后的操作
        print('exiting context')

# 使用上下文管理器
with my_context():
    print('inside context')

定义了一个 my_context 上下文管理器,使用 @contextmanager 装饰器将其转换为一个生成器函数。在该生成器函数中,我们可以在 yield 语句前后添加上下文的进入和离开时的操作。在使用上下文管理器时,可以使用 with 语句来自动管理上下文,而不需要手动调用 __enter____exit__ 方法。

当执行 with my_context() 语句时,程序会进入 my_context 上下文,执行 entering context,然后执行 yield 语句前的代码,即 inside context。当程序从 with 语句块中退出时,会自动执行 finally 语句块中的代码,即 exiting context

从生成器到上下文管理器

@contextmanager 装饰器可以帮助我们更轻松地创建上下文管理器,避免手动编写 __enter____exit__ 方法的繁琐。

需要主要的是这个函数必须是个装饰器
被装饰器装饰的函数分为三部分:
with 语句中的代码块执行前执行函数中 yield 之前代码
yield 返回的内容复制给 as 之后的变量
with 代码块执行完毕后执行函数中 yield 之后的代码

再简单理解就是
yield 前半段用来表示__enter__()
yield 后半段用来表示__exit__()

from contextlib import contextmanager

@contextmanager
def open_func(file_name):
    # __enter__方法
    print('open file:', file_name)
    file_handler = open(file_name, 'r')

    yield file_handler  # 这里的 yield 用于返回一个生成器

    # __exit__方法
    print('close file:', file_name)
    file_handler.close()
    return


with open_func(r'filename.txt') as file_in:
    for line in file_in:
        print(line)
        

# 执行结果        
open file: filename.txt
12344567890
close file: filename.txt

使用继承类管理上下文

ContextDecorator

contextlib.ContextDecorator 是一个抽象基类,通过继承 contextlib 里面的 ContextDecorator 类,用来定义上下文管理器装饰器。使用它可以使得上下文管理器可以像装饰器一样使用。

我们可以通过继承 ContextDecorator 类来创建一个上下文管理器装饰器。

from contextlib import ContextDecorator

class my_decorator(ContextDecorator):
    def __enter__(self):
        print('Entering the context')
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print('Exiting the context')
        return False

@my_decorator()
def my_function():
    print('Inside the function')

my_function()
# 直接结果
Entering the context
Inside the function
Exiting the context

关闭open的句柄

并不是所有的类都支持上下文管理器的 API,有一些遗留的类会使用一个 close 方法。
为了确保关闭句柄,需要使用 closing 为他创建一个上文管理器。

import contextlib

class Http():
    def __init__(self):
        self.session = "open"
        print("init: set open")

    def close(self):
        """
        关闭的方法必须叫 close
        """
        self.session = "close"
        print("set close")


if __name__ == '__main__':
    with contextlib.closing(Http()) as http:
        print(f"inside session value:{http.session}")
    print(f"outside session value:{http.session}")

    with contextlib.closing(Http()) as http:
        print(f"inside session value:{http.session}")
        raise EnvironmentError("EnvironmentError")
    print(f"outside session value:{http.session}")
    
    
#  直接结果
init: set open
inside session value:open
set close
outside session value:close
init: set open
inside session value:open
set close
Traceback (most recent call last):
  File "D:/Note/lcodeNoteCards/testcode/python/testpy.py", line 24, in <module>
    raise EnvironmentError("EnvironmentError")
OSError: EnvironmentError

即使程序出现了错误,最后也会执行 close 方法的内容。

参考资料

参考资料1

标签:__,管理器,exc,python,value,contextlib,print,上下文
From: https://www.cnblogs.com/tian777/p/17527660.html

相关文章

  • XMU Python语法
     题解:这道题重点是行号和列号!千万!别搞反了,还有就是用dx和dy数组表示顺时针转动1dx=[-1,0,1,0]2dy=[0,1,0,-1]3n,m=map(int,input().split())#n行m列4x,y,d=0,0,156#注意x为行标y为列标78#先执行前然后执行......
  • Python中使用支付宝支付
    准备#支付宝文档https://opendocs.alipay.com/open/270/105898?pathHash=b3b2b667#在沙箱环境下实名认证https://openhome.alipay.com/platform/appDaily.htm?tab=info#完成RSA密钥生成#1下载密钥工具#2生成密钥https://opendocs.alipay.com/common/......
  • Python基础37 基于tcp、udp套字编程、粘包现象、struct模块
    基于tcp协议的套接字编程(sochet编程)什么是socket?通常翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把tcp/ip层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中。套接字分类:AF_UNIX:用在局域网中AF_INET:用在互联网中客户端和服务端启动顺序......
  • python给多页excel工作表写跳转目录及回转链接
    1fromopenpyxlimportload_workbook2#fromopenpyxl.drawing.textimportParagraph,RegularTextRun3fromopenpyxl.worksheet.hyperlinkimportHyperlink4#fromopenpyxl.utilsimportget_column_letter56#打开现有的工作簿7workbook=load_workbo......
  • python之pip
    #####################   查看当前环境已经安装了哪些包:piplist   查看安装包详情  安装pip_search    使用pip_search           ########################......
  • python索引
    变量名[]正向数时是从零开始,反向是从-1开始切片变量[头下标:尾下标](不包括尾下标所代表的字符)变量名[:]:不指定头下标和尾下标时代表获取整个字符串变量名[1:]:不指定尾下标时代表从指定的头下标到末尾变量名[:5]:不指定头下标时代表从头开始到尾下标指定的字符但不包含尾下标所......
  • python网络编程 socket
    基于TCP协议的套接字编程(socket编程)什么是Socket呢?我们经常把Socket翻译为套接字,Socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。套接字的分类:AF_UNIX:用在局域网中AF_INET:用在互联网中"""客户端和......
  • 为组态王编写的 时间段 选择 控件 python
    日历控件使用说明这是一个专门为组态软件(如组态王,力控等)设计的时间选择控件,用于选择一个时间段,来进行数据报表的查询.控件实际由2部分组成,1个UI程序,和1个modbusTCP从机服务器.从机服务器用于UI程序和组态软件的通信.  日期部分,时间间隔部分,支持滚轮改变日期......
  • Python | yield关键字详解
    yield关键字的说明yield是Python中的一个关键字,它通常与生成器函数一起使用。yield就是保存当前程序执行状态。你用for循环的时候,每次取一个元素的时候就会计算一次。用yield的函数叫generator,和iterator一样,它的好处是不用一次计算所有元素,而是用一次算一次,可以节省......
  • python函数外变量传到函数内处理后不改变函数外的变量,copy模块使用
    线上代码a=[1,2,3]defabc(a):a.remove(1)abc(a)print(a)这段代码先指定了一个a变量是个list,又写了一个abc函数,功能是把外面传进来的list里面的1这个值去掉按理说在函数内的执行只应该属于函数内的变化,但是实际打印结果是[2,3],函数把外面变量的1删掉了这不是我想要......