用于包装任意代码块的可重用Python功能: Python上下文管理器
上下文管理器是包装任意(自由格式)代码块的工具。使用上下文管理器的主要原因之一是资源清洁度。上下文管理器确保进程在进入和退出时稳定执行,它释放资源。即使包装的代码引发异常,上下文管理器也会保证退出。因此,没有任何拖延,让我们潜入并获得资源清洁的新口头禅,而无需重复代码。
注意:感谢有关装饰器和生成器的知识
上下文管理器语法
众所周知,释放资源的正确方法是在使用后关闭资源。构建关闭函数的最常见做法是使用异常处理。让我们看看下面的代码。
def main():
try:
file = open('sample.txt', 'r')
data = file.read()
print(data)
finally:
file.close()
main()
finally 子句确保文件将在任何情况下关闭。但是这个过程会导致代码重复。那么,如何在没有样板设置的情况下使用上下文管理器来实现相同的目标。让我们一一浏览以下部分:
- with 语句
- 进出方法
- 异常处理
with 语句
使用语句, Python内置函数可以用作上下文管理器。
with open (‘filename’, ’r’) as file:
data = file.read()
此代码执行的功能与我们使用异常处理开发的功能相同。这里的 with 语句应该返回一个带有两个魔术方法的对象:__enter__ 和 __exit__。 __enter__ 方法返回的结果被赋值给 `as` 关键字后面提到的变量。
它处理包装代码中是否引发异常,最重要的是该语句确保资源被释放。因此,程序员不需要执行关闭操作。这是完整的代码。
def main():
with open('sample.txt', 'r') as file:
data = file.read()
print(data)
main()
进出方法
__enter__ 方法返回一个对象,然后将返回的值赋给 `as` 关键字后面提到的变量。除了 self 参数之外,它不需要其他参数。
另一方面, __exit__ 方法接受除 self 参数之外的 3 个位置参数。默认情况下,这三个参数都没有,并在包装代码引发异常时填充信息。
下面的代码解释了上下文管理器的工作流程。记下内部属性以及属性值在不同场景中的变化方式。进入上下文管理器后,属性值设置为 true,退出时,其值设置为 false。一旦 with 语句中的代码块完成执行,就会调用 __exit__ 方法。
class ContextCheck(object):
def __init__(self):
self.inside = False
def __enter__(self):
self.inside = True
return self
def __exit__(self, exc_type, exc_instance, traceback):
self.inside = False
cntCheck = ContextCheck()
print(cntCheck.inside)
with cntCheck:
print(cntCheck.inside)
print(cntCheck.inside)
输出
False
True
False
异常处理
使用上下文管理器如何处理异常?
使用 __exit__ 方法,上下文管理器处理包装代码引发的异常。 __exit__ 方法有 3 个位置参数:
- 异常类型
- 异常的一个实例
- 回溯选项
默认情况下,所有三个值都是无。当 __exit__ 方法收到异常时,该方法可以通过 2 种不同的方式处理异常:
- 重新引发异常
- 抑制异常
重新引发异常
__exit__ 方法可以通过返回 False 的 return 语句重新引发异常。让我们看看下面的代码:
class BubbleExc(object):
def __enter__(self):
return self
def __exit__(self, ex_type, ex_instance, traceback):
if ex_instance:
print('Exception: % s.' % ex_instance)
return False
with BubbleExc():
1 / 0
输出
Exception: division by zero.
Traceback (most recent call last):
File "C:\Users\Sonu George\Documents\Python\Context Managers\bubbleExc.py", line 11, in
1/0
ZeroDivisionError: division by zero
抑制异常
__exit__ 方法可以通过返回 true 来抑制异常。请参阅下面的示例以了解详细信息。
class SuppressExc(object):
def __enter__(self):
return self
def __exit__(self, ex_type, ex_instance, traceback):
if ex_instance:
print('Suppressing exception: % s.'% ex_instance)
return True
with SuppressExc():
1 / 0
输出>
Suppressing exception: division by zero.
何时应该编写上下文管理器
- 释放资源:释放资源通常以样板代码告终。使用上下文管理器,程序员可以在 __enter__ 方法中打开资源并在 __exit__ 方法中关闭它,并且可以按需重用功能。
- 避免重复:使用 except 子句进行异常处理会导致重复代码。使用上下文管理器,程序员可以引发或抑制异常,重要的是它被定义在一个地方,因此可以避免重复。
编写上下文管理器
Knowing is not enough; we must apply. Willing is not enough; we must do.
– Johann Wolfgang von Goethe
将上下文管理器实现为类
使用上下文管理器写入文件的示例方法。这是完整的代码。
class FileClass(object):
def __init__(self, f_name, func):
self.f_obj = open(f_name, func)
def __enter__(self):
return self.f_obj
def __exit__(self, type, value, traceback):
self.f_obj.close()
with FileClass('sample.txt', 'w') as f_open:
f_open.write('Congratulations, Good Work !')
输出
Congratulations, Good Work!
使用上下文管理器关闭页面
此处 page.close() 将在完成读取页面后调用(从 with 块退出时)。
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('https://www.geeksforgeeks.org/')) as page:
for ln in page:
print(ln)
输出
使用上下文管理器作为函数装饰器和生成器
使用Python contexlib模块,您可以将上下文管理器实现为装饰器,并且方法中的yield语句可以灵活地将其用作生成器。要理解这个程序,你应该有关于装饰器和生成器的先验知识。
from contextlib import contextmanager
@contextmanager
def open_file(name):
try:
print('File Open')
f = open(name, 'r')
yield f
finally:
print('Close Operation')
f.close()
def main():
with open_file('sample.txt') as f:
data = f.read()
print(data)
main()
这里@contextmanager包装了open_file方法。当函数open_file被调用时,上下文管理器的 __enter__ 方法执行并将控制权传递给open_file方法,在该方法中文件打开并产生对可调用的文件引用with open_file('sample.txt') as f
并且在执行之前暂停执行最后阻塞。
一旦 with 语句中的代码被执行,它会将控制权交还给open_file并从停止的地方开始执行。在这种情况下, finally块执行并关闭文件。执行完成后,调用 __exit__ 方法并处理包装代码引发的任何异常。
通过检查异常类的实例来处理异常
在这种情况下,我们将检查异常是否是异常类的实例。下面我们将创建一个子类 'TypeErrorSubclass',它派生自 'TypeError' 异常类,并使用 raise 语句我们将引发 'TypeErrorSubclass' 异常。这是完整的代码。
class TypeErrorSubClass(TypeError):
pass
class ExceptionClass(object):
def __enter__(self):
return self
def __exit__(self, ex_type, ex_instance, traceback):
# Return True if there is no exception
if not ex_type:
return True
# Return True if execution type is
# equal or a subclass of TypeError
if issubclass(ex_type, TypeError):
print('Handling TypeError: % s' % ex_instance)
return True
return False
with ExceptionClass():
raise TypeErrorSubClass('Type Error')
输出
Handling ValueError: Type Error
__exit__ 方法使用issubclass(ex_type, TypeError)
检查引发的异常是否是 'TypeError' 类的实例,如果是实例,则通过返回 True 来抑制异常。
概括
上下文管理器确保资源被正确释放,其结构方式提供了在不同位置重用异常处理代码的灵活性,从而避免代码重复。上下文管理器用于包装任意代码块。但是,可以将上下文管理器用作装饰器,这避免了为某些任务单独编写上下文管理器和装饰器的需要。