Python编程时用到with上下文管理器的地方很多,但大多数人只知道怎么用却没有深入了解它。在此我尝试对Python上下文管理器with进行深入讲解,希望能帮到大家更好和更深入地掌握with的用法。
with作用
with语句是上下文管理器,上下文管理器(是一个对象)定义了在进入和退出with语句时需要执行上下文代码块。完整的上下文管理器必须有以下两个方法:
object.__enter__(self)
进入with语句块时会自动运行该对象的__enter__方法,如果有as子句那么with语句将将此方法的返回值绑定到as指定的目标。
object.__exit__(self, exc_type, exc_value, traceback)
退出with语句块时会自动执行此对象的__exit__方法,三个参数分别描述了导致上下文退出的异常。 如果上下文是无异常地退出的那么这三个参数都将为None。如果希望遇到异常时不抛异常那么需要return True;否则会正常流程处理。
注意:Python解释器会保证执行with语句时只要__enter__方法未报错那么__exit__方法总是会被调用。
with应用场景
在很多具有打开和关闭动作的场景都可以用with语句,常见的应用场景有打开和关闭文件、线程池、进程池、socket等等。
以下是使用with和不使用with的代码对比:
-
不使用with打开文件的代码:
try: f = open('z.txt', mode='rt') print(f.read()) except FileNotFoundError: print('文件不存在') finally: f.close()
-
使用with语句打开文件的代码:
with open('a.txt', mode='rt') as f: print(f.read())
通过对比我们可以发现使用with可以让代码简洁优雅!
with特殊用法
with语句可以嵌套使用,也可以单句复用。下面列举一个读写文件的案例。
读取a.txt文件的内容,将其追加到b.txt文件中:
-
嵌套使用with:
with open('a.txt', mode='rt') as r: with open('b.txt', mode='a+') as w: result = '\n'+r.read() w.write(result)
-
单句使用with:
with open('a.txt', mode='rt') as r, open('b.txt', mode='a+') as w: result = '\n'+r.read() w.write(result)
两种用法结果完全一致,我更推荐第二种用法,代码更加简洁优雅。
with自定义
下面写了一个自定义的OpenFile类,用来演示with自定义上下文管理器的方法。望能起到抛砖引玉的效果,让大家学会with上下文管理器的自定义方法。
-
with自定义OpenFile类打开文件:
import io class OpenFile(io.TextIOWrapper): def __init__(self, name, mode='rt'): self.code = True try: self.f = open(name, mode) except IOError: self.code = False def __enter__(self): if self.code: return self.f else: return False def __exit__(self, exc_type, exc_val, exc_tb): if self.code: self.f.close() return True with OpenFile('c.txt') as f: if f: print(f.read()) else: print('文件打开错误') print('后续代码')
说明:
使用with自定义OpenFile类打开文件时,如果该文件存在则会将文件句柄赋值给as的目标;如果该文件不存在那么会将False赋值给as的目标。
在with语句内部,我们可以通过判断f的值来执行正确的分支。
使用with自定义OpenFile类打开文件时无论遇到何种情况代码都可以执行完毕不报错。
-
系统默认with open方式打开文件代码:
with open('c.txt') as f: if f: print(f.read()) else: print('文件打开错误') print('后续代码')
说明:
使用系统默认的with open方式打开文件时,如果文件存在那么所有代码可以正常执行完毕。
如果文件不存在在open时就会抛Traceback异常,后续的代码都不会执行。