1.with语句
with语句实际上是一个非常通用的结构,允许我们使用上下文管理器。
2.什么是上下文管理器?
上下文管理器是一个对象,它支持两种方法:__enter__()和__exit__()。
如果想让一个对象可以使用with语句,必须实现实现它的__enter__()和__exit__()方法。
3.网络连接的例子
# -*- coding=utf-8 -*-
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 = family
self.type = type
self.sock = None
def __enter__(self):
if self.sock is not None:
raise RuntimeError('网络已连接')
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
LazyConnection类初始化并没有建立一个连接,连接的建立与销毁通过with语句来自动完成。
import pprint
if __name__ == "__main__":
connect = LazyConnection(('www.httpbin.org', 80))
with connect as s:
s.send(b'GET /user-agent HTTP/1.1\r\n')
s.send(b'host:www.httpbin.org\r\n')
s.send(b'\r\n')
response = s.recv(8196)
response = str(response).replace('\\r\\n', '')
pprint.pprint(response)
httpbin.org 这个网站能测试 HTTP 请求和响应的各种信息,比如 cookie、ip、headers 和登录验证等,且支持 GET、POST 等多种方法,对 web 开发和测试很有帮助。
有关httpbin具体信息可以查看其官网httpbin.org。
我们这里模拟HTTP发送请求。
返回的响应:
输出重新排版,方便阅读:
(HTTP/1.1 200 OK
Date: Fri, 08 May 2020 05:26:40 GMT
Content-Type: "
application/jsonContent-Length: 25
Connection: keep-alive
Server:'gunicorn/19.9.0'
Access-Control-Allow-Origin: ''
Access-Control-Allow-Credentials: true)
代码说明
我们的代码会被放到with语句块中执行。
当出现with语句的时候,调用对象的__enter__()方法会被调用,它返回的值(如果有)会被赋给as声明的变量。然后with语句块里面的代码开始执行。最后,__exit__()方法被触发进行清理工作。
不管with代码块中发生了什么(即使是异常),代码都会继续执行。
方法__enter__()不接受任何参数,在进入with语句时被调用,返回值被赋给关键字as后声明的变量。
方法_exit__()接受三个参数:异常类型(exc_type)、异常对象(exc_val)和异常跟踪(exc_tb)。在with语句块结束时被调用。
_exit__()方法可以自己决定如何利用这些异常信息,比如打印出来,或者对异常进行处理;
也可以选择忽略异常,返回None值,这样会抑制所有的异常;
如果返回True,那么异常会被清空,with语句块后面的程序会继续正常执行。
4.网络连接的改进版
上述的网络连接无法嵌套使用,即在with语句里继续嵌套使用with语句,无法建立多个连接。
下面是改进版:
# -*- coding=utf-8 -*-
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 = family
self.type = type
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() # 从栈中弹出最后一个连接并关闭它
5.应用场景
在需要管理一些资源比如文件、网络连接和锁的编程环境中,使用上下文管理器是很普遍的。 这些资源的一个主要特征是它们必须被手动的关闭或释放来确保程序的正确运行。
我们打开一个文件后,可能会忘记关掉它,但是with语句会解决这个问题。