PEP 497 – 向后兼容的标准机制
- 作者:
- Ed Schofield <ed at pythoncharmers.com>
- PEP 代理:
- Brett Cannon <brett at python.org>
- 状态:
- 已拒绝
- 类型:
- 流程
- 创建:
- 2015年8月4日
拒绝通知
指导委员会认为,该提案的__past__
方面过于复杂,其潜在益处不足以支撑。关于加强向后兼容性要求的另一部分,应由PEP 387解决。
范围
本 PEP 补充了 PEP 5、236 和 387,并具有类似的目标。
本 PEP 解释了在支持PEP 5,“语言演化指南”时,需要额外的兼容性机制。 PEP 236,“回到__future__
”,引入了向前兼容的机制以支持PEP 5,但指出向后兼容的新机制不在该 PEP 的范围之内。一个相关的 PEP(正在进行中)引入了这样的向后兼容机制。
PEP 5,“语言演化指南”,指出“本 PEP [PEP 5] 并不取代或排除其他兼容性策略,例如动态加载向后兼容的解析器。”
上下文
摘自PEP 236:“有时,Python 会对核心语言结构的已公布语义进行不兼容的更改,或者以某种方式更改其意外(实现相关的)行为。虽然这绝非随意进行,并且始终是为了长期改进语言而进行的,但在短期内,它是有争议且具有破坏性的。PEP 5,语言演化指南,建议了一些缓解痛苦的方法,而本 PEP [PEP 236] 引入了一些支持该方法的机制。”
同样摘自PEP 236:“future_statement 的目的是让及时了解最新版本的人们更容易一些。如果您不这样做,我们不会讨厌您,但您的问题更难解决,并且遇到此类问题的人需要编写一个 PEP 来解决它们。future_statement 面向不同的受众。”
现状
当对核心语言语法或语义进行不兼容的更改时,Python 目前提供了 future_statement 机制,以便在强制执行新语法或语义的版本发布之前提供向前兼容性,但在此版本之后,没有提供相应的标准机制来提供向后兼容性。
问题
这种不对称性带来的后果是,关于破坏性更改,旧版(更改前的)Python 解释器比新版(破坏性更改的)解释器功能更强大;旧版解释器可以使用更改前设计的代码和更新的代码,而新版解释器只能使用已升级以支持更改的功能的代码。
例如,考虑 2001 年在PEP 238中引入的除法运算符的更改,这很快就在PEP 236引入 future_statement 机制之后。 PEP 238概述了一套有用的向前兼容机制,用于 Python 2.x 系列中的“真除法”,但省略了在 Python 3.0 中首次强制执行“真除法”之后的任何向后兼容机制。Python 3.0 之后的版本不提供向后兼容机制,例如from __past__ import division
,用于预期旧的“经典除法”语义的代码,而 Python 3.0 之前的版本确实支持“经典除法”代码以及预期“真除法”的代码的向前兼容性。另一个后果是,关于野外各种与除法相关的 Python 代码,最兼容的解释器是 Python 2.7,即首次强制执行破坏性更改之前的版本。
向后兼容性作为“向下升级”的推动因素
与此情况形成对比的是,办公套件等应用程序软件的新版本往往比早期版本更强大,因为它们支持加载不同版本的数据文件格式。模式通常是新版应用程序可以透明地加载来自其新版或旧版数据格式的数据,并且新版默认以新版格式保存数据。新版应用程序软件往往默认向后兼容。向前兼容性相对较少。
此策略使新版应用程序软件的用户比旧版软件的用户更有优势,旧版软件通常无法加载新版格式的数据。有时,新版软件应用程序的用户可以通过显式选择此选项来导出旧版数据。在这些情况下,由此带来的向前兼容性可能是完美的,也可能不是完美的;某些功能可能会丢失,或者结果可能会不尽如人意。因此,升级很容易,而降级则更难。
在许多用户中,由于这种新功能和向后兼容功能的策略而产生的行为是,每个用户都会自然而然地感受到升级其应用程序版本的压力,并且,个人与其他用户交换数据文件越多,这种压力就越强烈。
提案 - 第1部分
本 PEP 提出了两个具体且相关的提案。第一个是
示例
作为如何应用本 PEP 的示例,如果今天提出“真除法”PEP(238)的最新修订版,则会被认为是不完整的。 PEP 238 指出了提案引发的“严重的向后兼容性问题”,并在摘要和 API 更改部分描述了几种向前兼容措施。它还提到了在 c.l.py 上提出的一些向后兼容想法,包括“使用from __past__ import division
在模块中使用经典除法语义”,但它没有将任何向后兼容计划作为提案的一部分提出。
如果本 PEP 被接受,则预计类似于PEP 238的提案,由于其大规模的兼容性影响,也将附带一个向后兼容计划,使破坏性更改生效后的未来 Python 版本的用户能够轻松地在他们的代码中重新启用经典除法行为。
提案 - 第2部分
第二个提案是
Python 提供与__future__
模块机制并行的标准向后兼容机制,用于向前兼容。
作为参考,本文档将在本文中将其称为“__past__
”机制,尽管它不一定具有__future__
模块和future_statement
机制的所有特性。
__past__
机制的具体形式和实现是另一个 PEP(正在进行中)的主题。但是,本 PEP 建议设计此__past__
机制以满足PEP 296中为__future__
概述的类似标准。具体来说
a. 它应该使各个模块能够指定过时的行为,以便在模块基础上重新启用来自旧版 Python 版本的行为。
b. 它应该足够灵活,以便 Python 3.6+ 和早期版本的点版本能够为调用__past__
机制的用户模块重新引入与旧版 Python 语法或语义的向后兼容性。
c. 应该能够在旧版 Python 版本(如 2.x)上运行增强以调用__past__
行为的旧代码,这些版本不知道调用的特定__past__
功能,甚至不知道用于向后兼容性的__past__
机制的存在。
反例
违反这些标准的__past__
机制的一些实现是
a. 导入钩子。这些通常无法在模块基础上工作;相反,它们会递归地应用于从模块内部导入的所有新模块。
b. Python 3.6 中引入的新语法或新语义与先前版本不兼容。
c. 在 Python 3.6 中添加到 Python 标准库中模块中的函数,该函数在先前版本的 Python 中以相同的名称存在。
优势
采用本提案对 Python-dev 的好处是,如果这些更改都具有相应的__past__
功能,并且可以被未来 Python 版本的用户轻松调用,那么未来的向后不兼容更改可能会减少破坏性。这可以帮助语言更快、更有效地发展,以纠正设计错误。
对保守型用户的益处是显而易见的:他们只需向每个模块添加一个__past__
语句(可能是一行),就可以为其代码添加对最新闪亮且不兼容的 Python 版本的支持,并且可以自动执行此操作。然后,他们可以将其解释器升级到最新版本,并访问最新的闪亮 Python 功能。
对社区的好处是,如果一万名用户依赖于包 XYZ,而包 XYZ 可以轻松地添加对最新 Python 版本的支持,那么这 10,000 名用户也可以快速升级到最新 Python 版本,而无需等待包 XYZ 完成此操作。
问答
Q1:本 PEP 是否要求 Python 永久保留每个向后不兼容功能的两组可能的语义?
A1:当然不是。当合适的时候,遗留特性仍然可以逐步淘汰——也就是说,当大多数用户迁移到更新的 Python 版本时。此 PEP 仅仅建议将开发工作重点从 100% 向前兼容转移到至少 50% 向后兼容。对于允许用户采用最新的 Python 解释器版本,向后兼容性是两个概念中更强大的一个。
请注意,大多数用户已经很久没有关心非嵌套作用域的向后兼容性了,因为大多数用户已经轻松地跨越了 Python 2.1。
Q2:但是 Python 开发团队已经不堪重负,没有带宽来实现/维护额外的复杂性!
A2:Python 开发团队可以请求开发者社区站出来,维护 Python 中他们关心的遗留语言特性的向后兼容性。当社区不再关心某个特定的过时行为时,Python 开发团队也可以停止关心。
该 __past__
机制可能被设计为可由社区扩展,例如作为标准但“受认可的” PyPI 包,以减少核心开发人员的工作量。
Q3:向后兼容特性不会导致 Python 中出现大量无用代码、膨胀和包袱吗?
A3:不一定。首先,Python 中新的破坏兼容性的特性提案可以部分地根据其关联的 __past__
特性的实现的简单性和可维护性进行评估。
其次,一些旧特性很容易提供向后兼容性。考虑 Python 3.0 之前的“经典除法”行为。python-future
项目包含了经典除法在函数 future.utils.old_div
中的兼容实现。
def old_div(a, b):
"""
Equivalent to ``a / b`` on Python 2 without ``from __future__ import
division``.
"""
if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral):
return a // b
else:
return a / b
将这样的函数与 Python 3.x 版本捆绑在一起,并使用一个简单的机制在合适的 __past__
调用之后,为每次出现的 a / b
调用它,并不需要繁重的工作。
Q4:性能如何?在遗留特性的负担下,更新的 Python 版本的性能不会下降吗?
A4:这可以根据具体情况进行评估。主要潜在问题是,由于存在遗留选项,新默认行为的性能不会受到过大的影响。在 __past__
调用影响下的性能是次要的。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0497.rst
上次修改时间:2023-09-09 17:39:29 GMT