Python 3.9 新特性:任意表达式可做为装饰器!

一个月前(2月20日),一则新的 PEP 没有受到任何阻碍就被官方采纳了,这么快的速度,彷佛并很少见。html

然而,更为高效率的是,仅在半个月内,它的实现就被合入了代码仓。也就是说,咱们最快有望在 3 天后(3月23日)发布的 3.9.0 alpha 5 版本中看到它!python

Python 3.9 的发布计划:git

这个 PEP 就是 PEP-614:放宽对装饰器的语法限制。github

当前装饰器的语法为:express

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
复制代码

PEP-614 提议将其简化为:ide

decorator: '@' namedexpr_test NEWLINE
复制代码

我已经把 PEP 全文翻译出来了,Github 地址:dwz.date/RV9测试

放宽对装饰器的限制,这对以前的用法没有影响,但至于会带来哪些新的好处,我还不知道有哪些现实的例子。ui

下面是 PEP 翻译后的核心内容摘录,先跟你们一睹为快吧:idea

--------------摘录分割线----------------spa

概要

Python 当前要求全部装饰器都由 dotted name 组成,可选地带一个调用。本 PEP 提议消除这些限制,并容许任何有效的表达式做为装饰器。

(译注:dotted name,指的是装饰器在“@”符号后是“xxx”或“xxx.yyy”这种格式。没有很好地译法,故未译。)

动机

在最初引入装饰器时,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...
复制代码

当前,这些装饰必须重写成这样(译注:上方是假想的最优写法,但 Python 还不支持,只能用下方的啰嗦写法):

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 和反模式的 identity-function 之类的诱惑。

简而言之:若是要删除一些限制,咱们应该删除全部限制。

什么算一个“表达式”

在本文档中,“表达式”一词的用法与《Python语言参考》中定义的相同。能够归纳为“任何在 if、elif 和 while 块中测试为有效的内容”。

这与可能更流行的定义稍有不一样,后者能够归纳为“任何做为有效字符串输入给 eval 的内容”。

前一个“表达式”的定义更方便,由于它很是贴合咱们的需求,而且能够重用被现有语言结构所容许的语法。与其它定义相比,它有两个细微的差别:

一、元组必须加括号

这是基于 Guido 在同一封邮件中的洞察。紧接着前面的引述:

可是我不会容许逗号,决不可能同意这样:

@f, g
def pooh(): ...
复制代码

确实,它可能甚至致使没有经验的读者得出结论,认为正在使用多个装饰器,就像它们被堆叠了同样。这里要求加括号,可使意图变得清晰,而无需施加进一步的限制和复杂语法。

二、赋值表达式不需括号

在这里,语法的选择是明确的。PEP 572解释了为何须要在顶级表达式语句的周围加上括号:

加入此规则是为了简化用户在赋值语句和赋值表达式之间的选择——没有令二者都生效的语法位置。

因为赋值语句在此处无效,所以赋值表达式就没必要带括号。

(译注:赋值表达式,即 Assignment Expressions 或 Named Expressions,是 Python 3.8 引入的新特性,就是它引入了新的“:=”海象操做符。)

-----------------正文分割线---------------

PEP 的全文翻译已收录在 Github 的《PEP中文翻译计划》中,目前已有 20+ 篇 PEP 翻译,欢迎感兴趣的同窗查阅&参与翻译。

附录:

PEP614英文:www.python.org/dev/peps/pe…

PEP614中文:dwz.date/RV9

PEP中文翻译计划:github.com/chinesehuaz…

相关文章
相关标签/搜索