Python 3.9 新特性:任意表達(dá)式可作為裝飾器!

一個(gè)月前(2月20日),一則新的 PEP 沒有受到任何阻礙就被官方采納了,這么快的速度,似乎并不多見。

然而,更為高效率的是,僅在半個(gè)月內(nèi),它的實(shí)現(xiàn)就被合入了代碼倉。也就是說,我們最快有望在 3 天后(3月23日)發(fā)布的 3.9.0 alpha 5 版本中看到它!

Python 3.9 的發(fā)布計(jì)劃:

image

這個(gè) PEP 就是 PEP-614:放寬對裝飾器的語法限制。

當(dāng)前裝飾器的語法為:

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE

PEP-614 提議將其簡化為:

decorator: '@' namedexpr_test NEWLINE

我已經(jīng)把 PEP 全文翻譯出來了,Github 地址:http://dwz.date/RV9

放寬對裝飾器的限制,這對之前的用法沒有影響,但至于會(huì)帶來哪些新的好處,我還不知道有哪些現(xiàn)實(shí)的例子。

下面是 PEP 翻譯后的核心內(nèi)容摘錄,先跟大家一睹為快吧:

--------------摘錄分割線----------------

概要

Python 當(dāng)前要求所有裝飾器都由 dotted name 組成,可選地帶一個(gè)調(diào)用。本 PEP 提議消除這些限制,并允許任何有效的表達(dá)式作為裝飾器。

(譯注:dotted name,指的是裝飾器在“@”符號(hào)后是“xxx”或“xxx.yyy”這種格式。沒有很好地譯法,故未譯。)

動(dòng)機(jī)

在最初引入裝飾器時(shí),Guido表示對其語法作限制是一種偏好,而不是因?yàn)榧夹g(shù)的要求:

我對此有一種直覺。我不確定它來自哪里,但我就是有……因此,盡管將來將語法更改為 @test 相當(dāng)容易,但我仍想堅(jiān)持使用更受限的形式,除非給出了真正的使用 @test 會(huì)增加可讀性的用例。

盡管在實(shí)踐中很少遇到問題,但是多年來,BPO問題郵件列表帖子不斷出現(xiàn),要求去除限制。最近的一封郵件(它促成了本提案)提供了一段很好的使用 PyQt5 庫的示例代碼,如果放寬現(xiàn)有的限制,它將變得更具可讀性、地道性和可維護(hù)性。

稍作修改的示例:

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...

當(dāng)前,這些裝飾必須重寫成這樣(譯注:上方是假想的最優(yōu)寫法,但 Python 還不支持,只能用下方的啰嗦寫法):

button_0 = buttons[0]

@button_0.clicked.connect
def spam():
    ...

button_1 = buttons[1]

@button_1.clicked.connect
def eggs():
    ...

此外,當(dāng)前的語法太過寬松,以至于無法將更復(fù)雜的裝飾器表達(dá)式結(jié)合在一起。也就是說,當(dāng)前的限制并沒有像預(yù)期的那樣去禁止任意復(fù)雜的表達(dá)式,而是使它們變得更丑陋且效率低下:

# Identity function hack:

def _(x):
    return x

@_(buttons[0].clicked.connect)
def spam():
    ...

# eval hack:

@eval("buttons[1].clicked.connect")
def eggs():
    ...

原理

允許任意表達(dá)式

在相當(dāng)長的一段時(shí)間內(nèi),允許任意有效表達(dá)式的決定(而不僅僅是放寬當(dāng)前的限制,如允許取下標(biāo)),已被視為裝飾器語法發(fā)展的下一個(gè)順理成章的步驟。正如Guido 在另一個(gè)郵件列表討論中所說

我覺得強(qiáng)制約束它沒有什么道理,因?yàn)樗巡辉偈且粋€(gè)普通的表達(dá)式。

若對語法進(jìn)行特殊設(shè)置以允許某些有用的用法,只會(huì)使當(dāng)前情況復(fù)雜化,并且?guī)缀跄芸隙ù诉^程會(huì)在將來的某個(gè)時(shí)間重復(fù)。此外,這種語法上的改變的目的之一是阻止使用上述的 eval 和反模式的 identity-function 之類的誘惑。

簡而言之:如果要?jiǎng)h除一些限制,我們應(yīng)該刪除所有限制。

什么算一個(gè)“表達(dá)式”

在本文檔中,“表達(dá)式”一詞的用法與《Python語言參考》中定義的相同。可以概括為“任何在 if、elif 和 while 塊中測試為有效的內(nèi)容”。

這與可能更流行的定義稍有不同,后者可以概括為“任何作為有效字符串輸入給 eval 的內(nèi)容”。

前一個(gè)“表達(dá)式”的定義更方便,因?yàn)樗浅YN合我們的需求,并且可以重用被現(xiàn)有語言結(jié)構(gòu)所允許的語法。與其它定義相比,它有兩個(gè)細(xì)微的差異:

1、元組必須加括號(hào)

這是基于 Guido 在同一封郵件中的洞察。緊接著前面的引述:

但是我不會(huì)允許逗號(hào),決不可能贊成這樣:

@f, g
def pooh(): ...

確實(shí),它可能甚至導(dǎo)致沒有經(jīng)驗(yàn)的讀者得出結(jié)論,認(rèn)為正在使用多個(gè)裝飾器,就像它們被堆疊了一樣。這里要求加括號(hào),可以使意圖變得清晰,而無需施加進(jìn)一步的限制和復(fù)雜語法。

2、賦值表達(dá)式不需括號(hào)

在這里,語法的選擇是明確的。PEP 572解釋了為什么需要在頂級表達(dá)式語句的周圍加上括號(hào):

加入此規(guī)則是為了簡化用戶在賦值語句和賦值表達(dá)式之間的選擇——沒有令兩者都生效的語法位置。

由于賦值語句在此處無效,因此賦值表達(dá)式就不必帶括號(hào)。

(譯注:賦值表達(dá)式,即 Assignment Expressions 或 Named Expressions,是 Python 3.8 引入的新特性,就是它引入了新的“:=”海象操作符。)

-----------------正文分割線---------------

PEP 的全文翻譯已收錄在 Github 的《PEP中文翻譯計(jì)劃》中,目前已有 20+ 篇 PEP 翻譯,歡迎感興趣的同學(xué)查閱&參與翻譯。

附錄:

PEP614英文:https://www.python.org/dev/peps/pep-0614/

PEP614中文:http://dwz.date/RV9

PEP中文翻譯計(jì)劃:https://github.com/chinesehuazhou/peps-cn

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容