每日学习2019-9-9
通过with触发的上下问管理协议的运行机制
with是我们常用的上下文管理运行机制,今天这篇博文主要用于理解上下文管理协议的运行机制,了解其机制后方便我们自己查看他人代码。
如何让对象可以兼容with语句
让一个python中的class可以支持with语句,必须要在其class中实现如下两个方法__enter__()与__exit__()两个方法。示例代码如下:
from socket import socket ,AF_INET,SOCK_STREAM
class LazyConnection:
def __init__(self,address,family=AF_INET,type=SOCK_STREAM):
self.address=address
self.family=AF_INET
self.type=SOCK_STREAM
self.sock=None
def __enter__(self):
if self.sock is not None:
raise RuntimeError ("Already connected!!!")
self.sock=socket(self.family,self.type)
self.sock.connect(self.address)
return self.sock
def __exit__(self, exc_type, exc_val, exc_tb):
self.sock.close()
self.sock=None
这个类的核心功能表示建立一条网络连接,但在初始状态下,它不会有任何动作,而是通过with开始建立网络连接的
代码实现如下:
from functools import partial
conn=LazyConnection(('www.python.org',80))
with conn as s:
s.send(b'GET /index.html HTTP/1.0\r\n')
s.send(b'Host:www.python.org\r\n')
s.send(b'\r\n')
resp=b''.join(iter(partial(s.recv,8192),b''))
print(resp)
当我们遇到一个with语句时,__enter__方法首先被执行,若__enter__方法中有返回值,则放置在as限定的变量中;之后开始执行with代码块中的其他内容,最后,exit__方法被触发来执行清理工作。
其中,exit()方法中的三个参数用于处理异常,包含异常类型、值、和对挂起异常的追溯。
exit()方法可以选择以某种方式来使用异常信息;但是,也可以什么都不做或干掉异常,返回None作为结果,**如果__exit()方法返回了True作为结果,则异常会被清理干净,就好像什么也没有发生一样。**
讨论
以上代码如果重复使用socket连接,代码就会产生异常,如果我们需要嵌套此socket连接应该如下形式实现
from socket import socket ,AF_INET,SOCK_STREAM
class LazyConnection:
def __init__(self,address,family=AF_INET,type=SOCK_STREAM):
self.address=address
self.family=AF_INET
self.type=SOCK_STREAM
#self.sock=None
self.connections=[]
def __enter__(self):
sock=socket(self.family,self.type)
sock.connect(self.address)
self.connections.append(sock)
return sock
def __exit__(self, exc_type, exc_val, exc_tb):
self.connections.pop().close()
self.sock=None
from functools import partial
conn=LazyConnection(('www.python.org',80))
with conn as s1:
...
with conn as s2:
...
第二个版本中,LazyConnection成为了一个专门生产网络连接的工厂类。内部实现中,我们把connections列表当作一个栈来使用,用于存储和保存连接。enter()开始执行时,会把新建连接存储于connections中,而__exit__()方法只会把最后一个新建的连接结束。
总结
上下文管理器最常用于管理类似文件、网络连接和锁这样的资源。这些资源全部有一个共同的特点,必须显示的进行关闭或释放才可以正常工作。