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
和标识函数反模式之类的技巧的诱惑。
简而言之:如果我们正在删除一些任意的限制,我们应该删除所有这些限制。
什么是“表达式”
在整个文档中,“表达式”一词的使用方式与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
上次修改时间:2023-09-09 17:39:29 GMT