目录
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:
# 执行语句
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/46085.html