Open zshuangyan opened 5 years ago
在flask代码中,到处都充斥着with ...的语句,实在读不下去了,决定对上下文管理器做个梳理。
上下文管理器允许你在有需要的时候,精确地分配和释放资源。 使用上下文管理器最广泛的案例就是with语句了。 想象下你有两个需要结对执行的相关操作,然后还要在它们中间放置一段代码。 上下文管理器就是专门让你做这种事情的。举个例子:
with open('some_file', 'w') as opened_file: opened_file.write('Hola!')
上面这段代码打开了一个文件,往里面写入了一些数据,然后关闭该文件。如果在往文件写数据时发生异常,它也会尝试去关闭文件。上面那段代码与这一段是等价的:
file = open('some_file', 'w') try: file.write('Hola!') finally: file.close()
当与第一个例子对比时,我们可以看到,通过使用with,许多样板代码(boilerplate code)被消掉了。 这就是with语句的主要优势,它确保我们的文件会被关闭,而不用关注嵌套代码如何退出。 上下文管理器的一个常见用例,是资源的加锁和解锁,以及关闭已打开的文件(就像我已经展示给你看的)。 让我们看看如何来实现我们自己的上下文管理器。这会让我们更完全地理解在这些场景背后都发生着什么。
一个上下文管理器的类,最起码要定义enter和exit方法。 让我们来构造我们自己的开启文件的上下文管理器,并学习下基础知识。
class File(object): def __init__(self, file_name, method): self.file_obj = open(file_name, method) def __enter__(self): return self.file_obj def __exit__(self, type, value, traceback): self.file_obj.close()
通过定义enter和exit方法,我们可以在with语句里使用它。我们来试试:
with File('demo.txt', 'w') as opened_file: opened_file.write('Hola!')
我们的exit函数接受三个参数。这些参数对于每个上下文管理器类中的exit方法都是必须的。我们来谈谈在底层都发生了什么。 with语句先暂存了File类的exit方法 然后它调用File类的enter方法 enter方法打开文件并返回给with语句 打开的文件句柄被传递给opened_file参数 我们使用.write()来写文件 with语句调用之前暂存的exit方法 exit方法关闭了文件
我们还没有谈到exit方法的这三个参数:type, value和traceback。 在第4步和第6步之间,如果发生异常,Python会将异常的type,value和traceback传递给exit方法。 它让exit方法来决定如何关闭文件以及是否需要其他步骤。在我们的案例中,我们并没有注意它们。 那如果我们的文件对象抛出一个异常呢?万一我们尝试访问文件对象的一个不支持的方法。举个例子:
with File('demo.txt', 'w') as opened_file: opened_file.undefined_function('Hola!')
我们来列一下,当异常发生时,with语句会采取哪些步骤。 它把异常的type,value和traceback传递给exit方法 它让exit方法来处理异常 如果exit返回的是True,那么这个异常就被优雅地处理了。 如果exit返回的是True以外的任何东西,那么这个异常将被with语句抛出。 在我们的案例中,exit方法返回的是None(如果没有return语句那么方法会返回None)。因此,with语句抛出了那个异常。
Traceback (most recent call last): File "<stdin>", line 2, in <module> AttributeError: 'file' object has no attribute 'undefined_function'
我们尝试下在exit方法中处理异常:
class File(object): def __init__(self, file_name, method): self.file_obj = open(file_name, method) def __enter__(self): return self.file_obj def __exit__(self, type, value, traceback): print("Exception has been handled") self.file_obj.close() return True with File('demo.txt', 'w') as opened_file: opened_file.undefined_function()
Output: Exception has been handled 我们的exit方法返回了True,因此没有异常会被with语句抛出。
在flask代码中,到处都充斥着with ...的语句,实在读不下去了,决定对上下文管理器做个梳理。
上下文管理器允许你在有需要的时候,精确地分配和释放资源。 使用上下文管理器最广泛的案例就是with语句了。 想象下你有两个需要结对执行的相关操作,然后还要在它们中间放置一段代码。 上下文管理器就是专门让你做这种事情的。举个例子:
上面这段代码打开了一个文件,往里面写入了一些数据,然后关闭该文件。如果在往文件写数据时发生异常,它也会尝试去关闭文件。上面那段代码与这一段是等价的:
当与第一个例子对比时,我们可以看到,通过使用with,许多样板代码(boilerplate code)被消掉了。 这就是with语句的主要优势,它确保我们的文件会被关闭,而不用关注嵌套代码如何退出。 上下文管理器的一个常见用例,是资源的加锁和解锁,以及关闭已打开的文件(就像我已经展示给你看的)。 让我们看看如何来实现我们自己的上下文管理器。这会让我们更完全地理解在这些场景背后都发生着什么。
基于类的实现
一个上下文管理器的类,最起码要定义enter和exit方法。 让我们来构造我们自己的开启文件的上下文管理器,并学习下基础知识。
通过定义enter和exit方法,我们可以在with语句里使用它。我们来试试:
我们的exit函数接受三个参数。这些参数对于每个上下文管理器类中的exit方法都是必须的。我们来谈谈在底层都发生了什么。 with语句先暂存了File类的exit方法 然后它调用File类的enter方法 enter方法打开文件并返回给with语句 打开的文件句柄被传递给opened_file参数 我们使用.write()来写文件 with语句调用之前暂存的exit方法 exit方法关闭了文件
处理异常
我们还没有谈到exit方法的这三个参数:type, value和traceback。 在第4步和第6步之间,如果发生异常,Python会将异常的type,value和traceback传递给exit方法。 它让exit方法来决定如何关闭文件以及是否需要其他步骤。在我们的案例中,我们并没有注意它们。 那如果我们的文件对象抛出一个异常呢?万一我们尝试访问文件对象的一个不支持的方法。举个例子:
我们来列一下,当异常发生时,with语句会采取哪些步骤。 它把异常的type,value和traceback传递给exit方法 它让exit方法来处理异常 如果exit返回的是True,那么这个异常就被优雅地处理了。 如果exit返回的是True以外的任何东西,那么这个异常将被with语句抛出。 在我们的案例中,exit方法返回的是None(如果没有return语句那么方法会返回None)。因此,with语句抛出了那个异常。
我们尝试下在exit方法中处理异常:
Output: Exception has been handled 我们的exit方法返回了True,因此没有异常会被with语句抛出。