使用Python打开文件可以这样写:
try:
file = open("error.log", "w")
# some sentences
except Exception:
pass
finally:
file.close()
这样在打开文件,进行一些处理的时候,就算抛出了异常,也能正常的关闭文件。
但我们可以用更简洁的方法来完成这一步骤:
with open("error.log", "w") as file:
# some sentences
这两个代码块实现的功能是等价的,但显然下面的更加简洁。这就是 上下文管理器。
当对象使用 with 声明创建时,上下文管理器允许类做一些设置和清理工作。上下文管理器的行为由下面两个方法所定义:
-
__enter__(self)
定义使用 with 声明创建的语句块最开始上下文管理器应该做些什么。注意 __enter__ 的返回值会赋给 with 声明的目标,也就是 as 之后的东西。 -
__exit__(self, exception_type, exception_value, traceback)
当 with 声明语句块执行完毕(或终止)时,执行该函数。它可以用来处理异常,进行清理,或者做其他应该在语句块结束之后立刻执行的工作。如果语句块顺利执行, exception_type , exception_value 和 traceback 会是 None 。如果出现异常,则被该函数捕获,你可以选择处理这个异常或者重新抛出让用户来处理。
一般来说,上下文管理器是这样定义的。这样我们可以直接用with
语句打开Example
的一个实例。
class Example:
def __init__(self):
pass
def __enter__(self):
return something
def __exit__(self, exception_type, exception_value, traceback):
something.close()
但有个魔幻的写法,如下:
class Closer:
"""一个上下文管理器,可以在with语句中使用close()自动关闭对象"""
def __init__(self, obj):
self.obj = obj
def __enter__(self):
return self.obj # 绑定到目标
def __exit__(self, exception_type, exception_value, traceback):
try:
self.obj.close()
except AttributeError: # obj没有close()方法
pass
这可以包裹一些有close()
方法但没有定义上下文管理的的object,来让这些object可以用with打开。
- 魔幻写法使用样例
import socket
with Closer(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as server:
server.bind(("0.0.0.0", 8000))
server.listen(120)
sock, address = server.accept()
socket
这个模块本身是没有定义上下文管理器的,但它有一个close()
方法需要在停止运行时调用。
而我们只需要一个Closer包裹住它,就可以在任何情况下,正确的关闭server。这样就不会出现明明这个程序停止运行了,8000端口还在被占用的情况。
网友评论