PEP 614 – 放宽装饰器语法限制
- 作者:
- Brandt Bucher <brandt at python.org>
- 发起人:
- Guido van Rossum <guido at python.org>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2020年2月10日
- Python 版本:
- 3.9
- 发布历史:
- 2020年2月11日,2020年2月18日,2020年3月3日
- 决议:
- Python-Dev 帖子
摘要
Python 目前要求所有装饰器都由一个带点名称组成,可选择后跟一个函数调用。本 PEP 建议取消这些限制,允许装饰器为任何有效表达式。
动机
当装饰器首次引入时,Guido 曾描述限制其语法的动机是一种偏好,而非技术要求:
我对这个有一个直觉。我不确定它来自哪里,但我有……所以,虽然将来将语法更改为@test会很容易,但我宁愿坚持使用更受限制的形式,除非出现允许@test会提高可读性的真实用例。
尽管这些限制在实践中很少遇到,但多年来 BPO 问题和 邮件列表帖子一直不断出现,请求取消这些限制。最近的一个(促成了本提案)包含了一个使用 PyQt5 库的代码的良好示例,如果放宽现有限制,该代码将变得更具可读性、更地道且更易于维护。略微修改后:
buttons = [QPushButton(f'Button {i}') for i in range(10)]
# Do stuff with the list of buttons...
@buttons[0].clicked.connect
def spam():
...
@buttons[1].clicked.connect
def eggs():
...
# Do stuff with the list of buttons...
目前,这些装饰必须重写为类似以下形式:
button_0 = buttons[0]
@button_0.clicked.connect
def spam():
...
button_1 = buttons[1]
@button_1.clicked.connect
def eggs():
...
此外,当前的语法已经足够宽松,以至于很容易通过修改将更复杂的装饰器表达式组合在一起。因此,当前的限制并没有像预期那样禁止任意复杂的表达式,而只是使它们变得更难看、效率更低。
# Identity function hack:
def _(x):
return x
@_(buttons[0].clicked.connect)
def spam():
...
# eval hack:
@eval("buttons[1].clicked.connect")
def eggs():
...
基本原理
允许任何表达式
允许*任何*有效表达式(而不仅仅是放宽当前限制以允许,例如,下标)的决定,作为装饰器语法演变的下一个逻辑步骤,已经考虑了相当长一段时间。正如 Guido 指出,在另一个邮件列表讨论串中:
我认为将其限制得比当前少但又比通用表达式多是不合理的。
特殊处理语法以允许*某些*有用情况只会使当前情况复杂化,并且几乎可以保证该过程将来会重演。此外,这种语法更改的目的之一是阻止使用上面所示的 eval 和恒等函数反模式等 hack 的诱惑。
简而言之:如果我们要移除一些任意的限制,我们就应该移除*所有*的限制。
什么是“表达式”
在本文档中,“表达式”一词的定义与《Python 语言参考》中相同。可以概括为“任何在 if、elif 和 while 块中作为测试有效的内容”。这与一个可能更流行的定义略有不同,后者可以概括为“任何作为字符串输入 eval 有效的内容”。
“表达式”的这个定义很方便,因为它很好地满足了我们的需求,并且重用了现有语言构造的允许语法。它与另一个定义有两个细微的区别:
元组显示*必须*用括号括起来
这基于 Guido 在同一封邮件中提出的一个观察。紧接着上面:
虽然我不允许逗号——不可能这样@f, g def pooh(): ...能讲得通。
事实上,它甚至可能导致经验不足的读者认为正在应用多个装饰器,就好像它们堆叠在一起一样。在这里要求使用括号可以清晰地表达(诚然是毫无意义的)意图,而不会施加进一步的限制和语法复杂性。
命名表达式*不需要*用括号括起来
在这里,语法的选择是明确的。PEP 572 解释了为什么它要求顶级表达式语句周围有括号:
包含此规则是为了简化用户在赋值语句和赋值表达式之间的选择——不存在两者都有效的语法位置。
由于此处赋值语句无效,赋值表达式不应不必要地加上括号。
规范
装饰器的语法目前是:
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
本 PEP 建议将其简化为:
decorator: '@' namedexpr_test NEWLINE
向后兼容性
这个新语法与现有语法完全向后兼容。
如何教授
装饰器可以继续像以往一样教授;普通的 Python 程序员可能并不知道当前存在这些限制。
参考实现
作者已编写了 CPython 实现。
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0614.rst