专栏首页sktjpython 类支持with调用

python 类支持with调用

enter exit

为了让一个对象兼容 with 语句,你需要实现 __enter__()__exit__() 方法。 例如,考虑如下的一个类,它能为我们创建一个网络连接:

<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">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('Already connected')
    self.sock = socket(self.family, self.type)
    self.sock.connect(self.address)
    return self.sock

def __exit__(self, exc_ty, exc_val, tb):
    self.sock.close()
    self.sock = None

</pre>

这个类的关键特点在于它表示了一个网络连接,但是初始化的时候并不会做任何事情(比如它并没有建立一个连接)。 连接的建立和关闭是使用 with 语句自动完成的,例如:

<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">from functools import partial

conn = LazyConnection(('www.python.org', 80))

Connection closed

with conn as s: # conn.enter() executes: connection open 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'')) # conn.exit() executes: connection closed </pre>

讨论

编写上下文管理器的主要原理是你的代码会放到 with 语句块中执行。 当出现 with 语句的时候,对象的 __enter__() 方法被触发, 它返回的值(如果有的话)会被赋值给 as 声明的变量。然后,with 语句块里面的代码开始执行。 最后,__exit__() 方法被触发进行清理工作。

不管 with 代码块中发生什么,上面的控制流都会执行完,就算代码块中发生了异常也是一样的。 事实上,__exit__() 方法的第三个参数包含了异常类型、异常值和追溯信息(如果有的话)。 __exit__() 方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个None值。 如果 __exit__() 返回 True ,那么异常会被清空,就好像什么都没发生一样, with 语句后面的程序继续在正常执行。

还有一个细节问题就是 LazyConnection 类是否允许多个 with 语句来嵌套使用连接。 很显然,上面的定义中一次只能允许一个socket连接,如果正在使用一个socket的时候又重复使用 with 语句, 就会产生一个异常了。不过你可以像下面这样修改下上面的实现来解决这个问题:

<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">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_ty, exc_val, tb):
    self.connections.pop().close()

Example use

from functools import partial

conn = LazyConnection(('www.python.org', 80)) with conn as s1: pass with conn as s2: pass # s1 and s2 are independent sockets </pre>

在第二个版本中,LazyConnection 类可以被看做是某个连接工厂。在内部,一个列表被用来构造一个栈。 每次 __enter__() 方法执行的时候,它复制创建一个新的连接并将其加入到栈里面。 __exit__() 方法简单的从栈中弹出最后一个连接并关闭它。 这里稍微有点难理解,不过它能允许嵌套使用 with 语句创建多个连接,就如上面演示的那样。

在需要管理一些资源比如文件、网络连接和锁的编程环境中,使用上下文管理器是很普遍的。 这些资源的一个主要特征是它们必须被手动的关闭或释放来确保程序的正确运行。 例如,如果你请求了一个锁,那么你必须确保之后释放了它,否则就可能产生死锁。 通过实现 __enter__()__exit__() 方法并使用 with 语句可以很容易的避免这些问题, 因为 __exit__() 方法可以让你无需担心这些了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • python pyqt5 QThread

    import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.Qt...

    用户5760343
  • python pyqt5 QInputDialog 数字选择下拉选择

    import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.Qt...

    用户5760343
  • python pyqt5 QFileDialog 常用

    getOpenFileName() getSaveFileName() setFileMode() setFilter()

    用户5760343
  • 自定义UITableViewCell实现左滑动多菜单功能LeftSwipe

    1、使用自定义UITableViewCell + UISwipeGestureRecognizer + 代理 实现;

    tandaxia
  • 里程碑式成果Faster RCNN复现难?我们试了一下 | 附完整代码

    【导读】2019年以来,除各AI 大厂私有网络范围外,MaskRCNN,CascadeRCNN 成为了支撑很多业务得以开展的基础,而以 Faster RCNN ...

    AI科技大本营
  • 教程 | 如何使用DeepFake实现视频换脸

    朱晓霞
  • Python高效编程(五)

    dreamkong
  • python练习题-day22

    1.编写程序, 编写一个学生类, 要求有一个计数器的属性, 统计总共实例化了多少个学生

    郭耀华
  • 图像处理: 超像素(superpixels)分割 SLIC算法

    超像素概念是2003年Xiaofeng Ren提出和发展起来的图像分割技术,是指具有相似纹理、颜色、亮度等特征的相邻像素构成的有一定视觉意义的不规则像素块。它利...

    Petrichor_
  • vn.py源码解读(五、主引擎代码分析----CTP模块)

            上一篇文章讲了MainEngine中的初始化函数,重点是DataEngine的讲解。有了对行情数据的处理,还需要有行情数据的来源。在MainEn...

    钱塘小甲子

扫码关注云+社区

领取腾讯云代金券