一个类只要实现了__enter__()和__exit__()
这个两个方法,通过该类创建的对象我们就称之为上下文管理器。
with语句 常规对文件进行IO操作我们使用的是open方法,后面为了简便和防止文件未关闭出现了更简单的用法,with open。
比如我们在文件读操作打开文件的时候,在里面进行了写操作,导致没执行到后面的文件关闭操作。
1 2 3 4 5 6 7 8 9 10 11 12 file = open ('log.log' , 'r' ) file.write("我要写入了" ) print ('能执行到这里吗' )file.close()
输出结果:
1 2 3 4 5 6 Traceback (most recent call last): File "/Users/tony/PycharmProjects/study/test.py", line 10, in <module> file.write("我要写入了") io.UnsupportedOperation: not writable Process finished with exit code 1
可以看到这种情况就导致了文件没有关闭,就需要我们对其进行异常处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 try : file = open ('log.log' , 'r' ) file.write("我要写入了" ) except Exception as e: print (e) finally : print ('能执行到这里吗' ) file.close()
输出结果:
1 2 3 4 not writable 能执行到这里吗 Process finished with exit code 0
改进后的代码即使出现了异常,我们最后也将文件给关闭了。
Python提供了 with 语句的这种写法,既简单又安全,并且 with 语句执行完成以后自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作 。
上下文管理器 上下文管理器可以使用 with 语句,with语句之所以这么强大,背后是由上下文管理器做支撑的 ,也就是说刚才使用 open 函数创建的文件对象就是就是一个上下文管理器对象。
自定义上下文管理器类,模拟文件操作:
定义一个File类,实现 __enter__() 和 __exit__()
方法,然后使用 with 语句来完成操作文件, 示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class File (object ): """文件操作类""" def __init__ (self, filepath, mode ): self.filepath = filepath self.mode = mode def __enter__ (self ): """打开文件""" self.file = open (self.filepath, self.mode) print ("打开文件" ) return self.file def __exit__ (self, exc_type, exc_val, exc_tb ): """关闭文件""" print ("关闭文件" ) self.file.close() if __name__ == '__main__' : with File('log.log' , 'r' ) as file: file.write("家啊" )
输出结果:
1 2 3 4 5 6 7 8 打开文件 关闭文件 Traceback (most recent call last): File "/Users/tony/PycharmProjects/study/test.py" , line 29 , in <module> file.write("家啊" ) io.UnsupportedOperation: not writable Process finished with exit code 1
可以看到,即使中间我们出现了异常,文件最后依然被关闭了。即:
__enter__
表示上文方法,需要返回一个操作文件对象__exit__
表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法。装饰器实现上下文 上面的方式为类实现上下文的,假如想要让一个函数成为上下文管理器,Python 还提供了一个 @contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 上面的语句在 __enter__
方法中执行,yield 下面的语句在 __exit__
方法中执行,紧跟在 yield 后面的参数是函数的返回值。
我们只需要在最开始的代码稍微改装下即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from contextlib import contextmanager@contextmanager def file_handle (filepath, mode ): """文件操作""" try : file = open (filepath, mode) yield file except Exception as e: print (e) finally : print ('能执行到这里吗' ) file.close() if __name__ == '__main__' : with file_handle('log.log' , 'r' ) as f: f.write("写入东西" )
输出结果:
1 2 3 4 not writable 能执行到这里吗 Process finished with exit code 0
总结
Python 提供了 with 语句用于简化资源释放的操作,使用 with 语句操作建立在上下文管理器(实现__enter__和__exit__
)的基础上 Python 还提供了一个 @contextmanager 装饰器,更进一步简化上下管理器的实现,让一个函数可以成为上下文管理器,结合 with 语句来使用