python基础 — 上下文管理器

一、初识上下文管理器

1、with语句

学习文件管理的时候我们都会用到with open(file) 语句来管理文件,在这个过程中文件一直保存打开的状态,这样做的好处是不用再单独去判断某种异常情况,也不用专门去执行文件关闭的指令了。

其实这里的with语句就是一个上下文管理器,语法结构如下:

with  上下文管理器对象 as f:

         expression

 

2、上下文管理器

试想一下,你打开一个文件,修改文件资源,保存并关闭修改后的文件资源。如果在关闭文件资源之前,你的代码发生了错误,很有可能导致文件不会被关闭,这会导致“资源泄漏”。在这种情况下,我们需要一个上下文管理器,保证无论代码是否出错,打开的'资源'最终都会被释放。

简单点说,就是在一个类里,实现了__enter__和__exit__的方法,这个类的实例就是一个上下文管理器。

1、__enter__() 进入上下文,返回当前对象或者与运行时上下文相关的其他对象。一般用来处理操作前的内容。比如一些创建对象,初始化等。如果with语句有as关键词存在,返回值会绑定在as后的变量上。

2、__exit__(exc_type, exc_val, exc_tb) 退出上下文,返回一个布尔值标示是否有需要处理的异常。一般用来处理一些善后收尾工作,比如文件的关闭,数据库的关闭等。如果在执行with语句体时发生异常,那退出时参数会包括异常类型、异常值、异常追踪信息,否则,3个参数都是None。

既然知道了上下文管理器要包含__enter__和__exit__方法,这个时候我们再来看下为什么说 with open(file) 语句就是一个上下文管理器了,

with open('mytest.txt','r') as f:
    print(dir(f))

-------------------------------------------------------------
运行结果:
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']

我们很容易看到对象f具有 __enter__ 和 __exit__ 方法,所以是上下文管理器。

 

3、为什么要使用上下文管理器

  • 更加优雅的方式操作(创建/获取/释放)资源,如文件操作、数据库连接;
  • 更加优雅的方式,处理异常;

二、自定义上下文管理器

1、 一个简单的上下文管理器

# 自定义上下文管理器
class MyContextManager:

    def __enter__(self):
        print("-- Starting the Manager --")

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("-- Exiting the Manager --")


with MyContextManager():
    print("-- In the Manager --")

------------------------------------------------------------------------
运行结果:
-- Starting the Manager --
-- In the Manager --
-- Exiting the Manager --

自定义一个上下文管理器 MyContextManager ,包含__enter__和__exit__方法。

分析运行结果可知其执行过程:

首先执行类中的__enter__()方法,它总是在进入代码块前被调用的

接着就执行代码块——with语句下面的代码块

离开的时候又调用类中的__exit__()

2、 接受参数的上下文管理器

# 自定义带参数的上下文管理器
class MyContextManagerOpenDemo:

    def __init__(self, filename, mode):
        """
        实例化对象的时候必须带上的参数
        """
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        """
        进入上下文管理器,打开指定文件,返回文件对象
        """
        print('--- Starting the Manager ---')
        self.open_file = open(self.filename, self.mode)
        return self.open_file

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        退出上下文管理器,关闭文件对象。
        """
        print('--- Exiting the Manager ---')
        self.open_file.close()

with MyContextManagerOpenDemo('mytest.txt', 'r') as f:
    print('--- In the Manager ---')
    print(f.read())
 
-----------------------------------------------------------------------------
运行结果:
--- Starting the Manager ---
--- In the Manager ---
aaaaaaaaaaaaaaaa
--- Exiting the Manager ---

注意:

上面的demo里没有考虑异常情况。

三、使用上下文管理器处理异常

上下文管理器包含的 __exit__ (exc_type, exc_val, exc_tb)方法中有三个参数,用来接收和处理异常,如果执行with语句时候发生异常,异常会被保存到这里。

  • exc_type : 异常类型
  • exc_val : 异常值
  • exc_tb : 异常回溯追踪

使用 return 返回值决定捕获的异常是否继续向外抛出(没有return 就默认为 return False)。

  • 如果是 False 那么就会继续向外抛出,程序会看到系统提示的异常信息,
  • 如果是 True 不会向外抛出,程序看不到系统提示信息,只能看到打印的输出

1、 抛出异常

 # 抛出异常
class MyContextManagerOpenDemo:

    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print('--- Starting the Manager ---')
        self.open_file = open(self.filename, self.mode)
        return self.open_file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('--- Exiting the Manager ---')
        self.open_file.close()
        # 抛出异常
        return False


with MyContextManagerOpenDemo('mytest.txt', 'r') as f:
    print('--- In the Manager ---')
    print(f.write('sss'))

---------------------------------------------------------------------------------
运行结果:
Traceback (most recent call last):
  File "C:/Users/057776/PycharmProjects/python-test/python_basics/context_manager/context_manager_test.py", line 82, in <module>
    print(f.write('sss'))
io.UnsupportedOperation: not writable
--- Starting the Manager ---
--- In the Manager ---
--- Exiting the Manager ---

分析可知,mode=f,只有“读”权限,没有“写”权限,with语句的f.write('sss')执行异常,__exit__方法捕获异常, return False 抛出异常信息 io.UnsupportedOperation: not writable

2、不抛出异常

# 不抛出异常
class MyContextManagerOpenDemo:

    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print('--- Starting the Manager ---')
        self.open_file = open(self.filename, self.mode)
        return self.open_file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('--- Exiting the Manager ---')
        self.open_file.close()
        print('Type: ', exc_type)
        print('Value:', exc_val)
        print('TreacBack:', exc_tb)
        # 不抛出异常
        return True


with MyContextManagerOpenDemo('mytest.txt', 'r') as f:
    print('--- In the Manager ---')
    print(f.write('sss'))

-------------------------------------------------------------------------
运行结果:
--- Starting the Manager ---
--- In the Manager ---
--- Exiting the Manager ---
Type:  <class 'io.UnsupportedOperation'>
Value: not writable
TreacBack: <traceback object at 0x0000016EBBD86040>

四、contextlib 模块

使用装饰器来实现上下文管理器,使我们的代码更加优雅,更python范儿。

这里不做介绍,后续会用专门的文章介绍 contextlib 模块。


reference:

上下文管理器 - 《零基础学python》(第二版)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值