【python】with...as语句——基于上下文管理器的操作

目录

with介绍

with语句如何工作

__exit__()方法说明

with中包含多个项


with语句简化公共资源的管理。使用with的代码更清晰,更具有可读性。

with介绍

先看一个写入文件的例子,常规的写入文件步骤是:打开文件;写入文件;关闭文件,代码如下:

# 常规的文件操作
fd = open('tmp.txt','w+')
fd.write('hello python')
fd.close()

如果在文件写入过程中出现异常,那么close()方法将无法被执行,tmp.txt文件会被程序一直占用无法被释放掉。

然后就会想到可以用捕获异常的方式来处理这种问题,代码如下:

# 异常捕获机制处理文件操作
try:
    fd = open('tmp.txt','w+')
    fd.write('hello python')
except Exception as e:
    print(e)
else:
    pass
finally:
    fd.close()

但是感觉代码有点冗余。如果使用with语句,代码如下:

# with语句进行文件操作
with open('tmp.txt','w+') as f:
    f.write('hello python')

使用两行语句即可处理。因为使用with关键字后系统会自动调用f.close()方法,with的作用等效于try/finally语句。

with语句的实现是建立在上下文管理器上的。

上下文管理器是一个实现_enter_和_exit_的方法的类。使用with语句可以确保在嵌套块的末尾调用_exit_方法。

上下文管理器首先调用__enter__方法,然后执行with语句中的代码,最后调用__exit__方法,即使出现错误,也会调用__exit__方法,即关闭文件流。

执行如下代码,显示结果,可以看到文件对象中实现了__enter__和__exit__方法,即文件对象也实现了上下文管理器

fd = open('tmp.txt','r')
print(dir(fd))
fd.close()

['_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']

with语句如何工作

  • 首先执行紧跟with的语句,如上示例中的 open('tmp.txt','w+'),该语句会返回一个对象,对象的__enter__方法被调用,__enter__方法的返回值赋值给as后面的变量;
  • 然后执行with后面的嵌套块中的语句;
  • 最后会调用前面返回的对象的__exit__方法;

为了更直观的说明,可以看如下实例

class MyManagement(object):
    
    
    def __enter__(self):
        print('__enter__ 被调用')
        return self

    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__ 被调用')
    

    def show(self):
        print('show 被调用')


if __name__ == '__main__':
    with MyManagement() as m:
        m.show()
    

输出结果,可以看到输出顺序是 __enter__()  →  嵌套块执行    →    __exit__()。

__enter__ 被调用
show 被调用
__exit__ 被调用

__exit__()方法说明

__exit__()方法中有三个参数,exc_type,exc_val,exc_tb,这三个参数和异常抛出相关联。当with嵌套块语句中有任何异常时,__exit__()方法都会被执行。

如下示例:
 

class MyManagement(object):
    
    
    def __enter__(self):
        print('__enter__ 被调用')
        return self

    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__ 被调用')
    

    def show(self):
        print('show 被调用',1/0)


if __name__ == '__main__':
    with MyManagement() as m:
        m.show()
    

在show()方法中,1/0会抛出异常,显示结果

__enter__ 被调用
__exit__ 被调用
Traceback (most recent call last):
  File "e:/1_tempFile/1_learn_code/VSCode/python_test/python_files/with.py", line 19, in <module>
    m.show()
  File "e:/1_tempFile/1_learn_code/VSCode/python_test/python_files/with.py", line 14, in show
    print('show 被调用',1/0)
ZeroDivisionError: division by zero

如果需要跳过某种异常,可以使__exit__()函数返回True。如下,跳过所有的TypeError,其它异常正常抛出。

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__ 被调用')
        return isinstance(exc_val,TypeError)

使用with语句对异常进行处理还可以参考如下写:

try:
    with open('a.txt') as f:
        # 执行语句
except xxxError:
    # 执行一些异常处理语句

with中包含多个项

如果有多个项,可以按照如下写:

with open('1.txt') as f1, open('2.txt') as f2:
    # 执行语句

参考:python的with用法 | kissdata

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值