PEP 3135 – 新式 super
- 作者:
- Calvin Spealman <ironfroggy at gmail.com>, Tim Delaney <timothy.c.delaney at gmail.com>, Lie Ryan <lie.1296 at gmail.com>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2007 年 4 月 28 日
- Python 版本:
- 3.0
- 更新历史:
- 2007 年 4 月 28 日, 2007 年 4 月 29 日, 2007 年 4 月 29 日, 2007 年 5 月 14 日, 2009 年 3 月 12 日
编号说明
该 PEP 起初是 PEP 367。由于它现在针对 Python 3000,所以它被移动到了 3xxx 命名空间。
摘要
该 PEP 提案为使用 super
类型提供了语法糖,以便自动构造 super 类型实例,并绑定到定义方法的类和正在执行方法的实例(对于类方法,则是类对象)上。
新 super 用法的基本前提如下
super().foo(1, 2)
用于替换旧的
super(Foo, self).foo(1, 2)
基本原理
当前 super 的用法需要显式地传递它必须从中操作的类和实例,这会导致违反 DRY(不要重复自己)原则。这阻碍了任何类名称的更改,并且经常被许多人视为缺点。
规范
在规范部分,将使用一些特殊的术语来区分相似和密切相关的概念。“super 类”将指代名为“super”的实际内置类。一个“super 实例”仅仅是 super 类的实例,它与另一个类和该类的实例(如果有的话)相关联。
新的 super
语义仅在 Python 3.0 中可用。
替换 super 的旧用法,可以调用 MRO(方法解析顺序)中的下一个类,而无需显式传递类对象(尽管这样做仍然受支持)。每个函数都将有一个名为 __class__
的单元,它包含定义函数的类对象。
新语法
super()
等效于
super(__class__, <firstarg>)
其中 __class__
是定义方法的类,而 <firstarg>
是方法的第一个参数(对于实例方法通常是 self
,对于类方法则是 cls
)。对于在类体之外定义的函数,__class__
未定义,这会导致运行时 SystemError
。
虽然 super
不是保留字,但解析器在方法定义中识别 super
的使用,并且仅在找到它时才传递 __class__
单元。因此,调用 super
的全局别名(无参数)不一定有效。
已解决问题
确定要使用的类对象
类对象取自名为 __class__
的单元。
super
应该真的成为关键字吗?
不。super 不需要成为关键字。
使用 __call__ 属性的 super
有人认为以经典方式实例化 super 实例可能是一个问题,因为调用它将查找 __call__ 属性,因此尝试对 MRO 中的下一个类执行自动 super 查找。但是,事实并非如此,因为调用对象仅直接在对象的类型上查找 __call__ 方法。以下示例显示了它的实际操作。
class A(object):
def __call__(self):
return '__call__'
def __getattribute__(self, attr):
if attr == '__call__':
return lambda: '__getattribute__'
a = A()
assert a() == '__call__'
assert a.__call__() == '__getattribute__'
无论如何,这个问题完全消失了,因为仍然支持对 super(<class>, <instance>)
的经典调用,其含义相同。
替代方案
不做改变
尽管保持现状总是很有吸引力,但人们一直都在寻求改变 super 的用法,而且有充分的理由,前面已经提到了所有这些理由。
- 与类名称解耦(类名称可能不再绑定到正确的类!)
- 外观更简洁、更干净的 super 调用会更好
super 类型上的动态属性
该提案在 super 类型中添加了动态属性查找,它将自动确定合适的类和实例参数。每次 super 属性查找都会识别这些参数,并在实例上执行 super 查找,就像当前的 super 实现使用对类和实例上的 super 实例的显式调用一样。
该提案依赖于 sys._getframe(),这不适合除原型实现之外的任何用途。
self.__super__.foo(*args)
该 PEP 在多个地方提到了 __super__ 属性,它可能是完整解决方案的候选者,实际上明确使用它而不是直接使用任何 super。但是,双下划线名称通常是内部细节,并且试图将其排除在日常代码之外。
super(self, *args) 或 __super__(self, *args)
该解决方案仅解决了类型指示的问题,没有处理不同命名的 super 方法,并且明确说明了实例的名称。它不太灵活,因为它无法在其他方法名称上执行,而这在需要的时候非常有用。该方案失败的一个用例是,当基类有一个工厂类方法,子类有两个工厂类方法时,两者都需要对基类中的工厂类方法进行适当的 super 调用。
super.foo(self, *args)
这种变体实际上消除了定位正确实例的问题,如果任何替代方案被推到聚光灯下,我希望是这个方案。
super(*p, **kw)
有人提议直接调用 super(*p, **kw)
等效于在名为与当前正在执行的方法相同的名称的 super
对象上调用该方法,即以下两种方法等效
def f(self, *p, **kw):
super.f(*p, **kw)
def f(self, *p, **kw):
super(*p, **kw)
对此有强烈的支持和反对意见,但实施和风格问题是显而易见的。Guido 建议根据 KISS(保持简单原则)将此排除在该 PEP 之外。
历史
- 2007 年 4 月 29 日
- 将标题从“Super 作为关键字”更改为“新式 super”
- 更新了大部分语言,并添加了一个术语部分,以便在令人困惑的地方进行澄清。
- 添加了参考实现和历史部分。
- 2007 年 5 月 6 日
- 由 Tim Delaney 更新,以反映在 python-3000 和 python-dev 邮件列表上的讨论。
- 2009 年 3 月 12 日
- 更新为反映当前的实现状态。
参考
[1] 修复 super,有人吗? (https://mail.python.org/pipermail/python-3000/2007-April/006667.html)
[2] PEP 3130:访问正在定义的模块/类/函数(此文档) (https://mail.python.org/pipermail/python-ideas/2007-April/000542.html)
版权
本文件已进入公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-3135.rst
最后修改时间:GMT 2023 年 9 月 9 日 17:39:29