Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 310 – 可靠的获取/释放对

作者:
Michael Hudson <mwh at python.net>,Paul Moore <p.f.moore at gmail.com>
状态:
已拒绝
类型:
标准轨迹
创建:
2002-12-18
Python 版本:
2.4
历史记录:


目录

摘要

能够有一种减少打字量的方式来编写以下代码会很好

the_lock.acquire()
try:
    ....
finally:
    the_lock.release()

本 PEP 提出了一段语法(“with” 块)和一个“小写 i” 接口,可以推广上述方法。

宣告

本 PEP 被拒绝,取而代之的是 PEP 343

基本原理

Python 异常处理理念的优势之一是,它使得难以做出“错误”的事情(例如,未能检查某些系统调用的返回值)。目前,这并不适用于资源清理。获取和释放资源(例如,锁)的当前语法是

the_lock.acquire()
try:
    ....
finally:
    the_lock.release()

这种语法将获取和释放分隔开一段(可能很大)的代码块,这使得难以“一目了然”地确认代码是否正确地管理了资源。另一个常见的错误是在 try 块中编写“获取”调用,如果获取失败,则会错误地释放锁。

基本语法和语义

“with” 语句的语法如下

'with' [ var '=' ] expr ':'
    suite

此语句被定义为等效于以下语句序列

var = expr

if hasattr(var, "__enter__"):
    var.__enter__()

try:
    suite

finally:
    var.__exit__()

__exit__ 方法的存在**不会**像 __enter__ 那样进行检查,以确保在 with: 语句中使用不合适的对象会产生错误)。

如果省略变量,则会在栈上分配一个未命名的对象。在这种情况下,代码块无法访问该未命名对象。

可能的扩展

在 Python 开发人员列表中讨论了许多对基本语法的潜在扩展。本 PEP 提出的解决方案中不包含任何这些扩展。在许多情况下,双方的论点几乎同样有力。在这种情况下,PEP 始终选择简单性,仅仅是因为在需要额外功能的地方,现有的 try 块可用。

多个表达式

一个提案是允许在一个“with”语句中使用多个表达式。 __enter__ 方法将从左到右调用, __exit__ 方法将从右到左调用。这样做的好处是,当管理多个资源时,嵌套的“with”语句会导致代码向右边缘漂移。解决此问题的方法与解决任何其他深度嵌套相同 - 将部分代码分解到单独的函数中。此外,需要解决以下问题:如果其中一个 __exit__ 方法引发异常会发生什么情况(是否应该调用其他 __exit__ 方法?)

异常处理

有人建议扩展协议以包含一个可选的 __except__ 处理程序,该处理程序在引发异常时被调用,并且可以处理或重新引发异常。目前尚不清楚此扩展的语义是否可以变得精确且易于理解。例如,如果定义了异常处理程序,则等效代码应该是 try ... except ... else ,如果没有定义异常处理程序,则应该是 try ... finally ?通常如何在编译时确定这一点?另一种选择是将代码定义为扩展到 try ... except 内部的 try ... finally 。但这在现实生活中可能无法正常工作。

为异常处理确定的唯一用例是事务处理(在干净完成时提交,并在出现异常时回滚)。这可能与使用传统的 try ... except ... else 块一样容易处理,因此 PEP 不包含任何对异常处理程序的支持。

实现说明

在指定为等效于 with 语句的代码中存在潜在的竞争条件。例如,如果在完成 __enter__ 方法调用和 try 块开始之间引发了 KeyboardInterrupt 异常,则不会调用 __exit__ 方法。这可能导致资源泄漏或死锁。[XXX Guido 已经表示他关心这种竞争条件,并打算编写一些 C 魔法来处理它们。 “with” 语句的实现应该复制此操作。]

未解决的问题

现有的类(例如,类文件对象和锁)是否应该获得适当的 __enter____exit__ 方法?支持的明显原因是方便(无需适配器)。反对的论点是,如果内置文件具有此功能,但(例如) StringIO 没有,则在文件对象上使用“with”的代码无法与 StringIO 对象一起重用。因此 __exit__ = close 成为“类文件对象”协议的一部分,用户定义的类可能需要支持该协议。

__enter__ 钩子可能是不必要的 - 对于许多用例,需要一个适配器类,在这种情况下, __enter__ 钩子完成的工作也可以在 __init__ 钩子中完成。

如果可以使用一种显式控制对象生命周期的方法,则 __exit__ 钩子的功能可以由现有的 __del__ 钩子接管。与该方法的支持者进行的电子邮件交流 [1] 使其中一位作者更加确信这不是正确的方法……

有人建议 [2] 将“__exit__”方法称为“close”,或者如果找不到 __exit__ 方法,则应考虑使用“close”方法,以提高“with …”结构的“开箱即用实用性”。

“with …”块和生成器在概念上有一些相似之处,这导致有人提议 for 循环可以实现 with 块功能 [3] 。虽然在某些层面上很巧妙,但我们认为 for 循环应该坚持作为循环。

替代方案

IEXEC:Holger Krekel – 使用类似 XML 的语法(未找到 URL……)的通用方法。

Holger 对“执行监视器”有更深远的构想,这些监视器了解被监视块中控制流的详细信息。虽然很有趣,但这些想法可能会以深刻而微妙的方式改变语言,因此属于不同的 PEP。

任何 Smalltalk/Ruby 匿名块样式扩展显然都包含此扩展。

PEP 319 处于同一领域,但在 python-dev 上发布时未获得支持。

向后兼容性

本 PEP 提出一个新的关键字,因此需要使用 __future__ 机制。

采用成本

那些声称语言越来越大越来越复杂的人有了一些新的抱怨对象。这也是需要额外教授的内容。

为了使该提案有用,标准库和其他代码中的许多类文件和类锁将必须具有

__exit__ = close

或类似的内容。

不采用成本

编写正确的代码仍然比编写错误的代码需要更多精力。

参考文献

这里可以提及各种 python-list 和 python-dev 讨论。


来源: https://github.com/python/peps/blob/main/peps/pep-0310.rst

上次修改: 2023-09-09 17:39:29 GMT