Python currently requires that all decorators consist of a dottedname, optionally followed by a single call. This PEP proposes removingthese limitations and allowing decorators to be any valid expression.
When decorators were first being introduced,Guido describedthe motivation to limit their syntax as a preference, not a technicalrequirement:
I have a gut feeling about this one. I’m not sure where it comesfrom, but I have it… So while it would be quite easy to changethe syntax to@testin the future, I’d like to stick to withthe more restricted form unless a real use case is presented whereallowing@testwould increase readability.
While these limitations were rarely encountered in practice,BPOissues andmailing list postshave consistently surfaced over the years requesting that they beremoved. Themost recent one(whichprompted this proposal)contained a good example of code using thePyQt5 library thatwould become more readable, idiomatic, and maintainable if theexisting restrictions were relaxed. Slightly modified:
buttons=[QPushButton(f'Button{i}')foriinrange(10)]# Do stuff with the list of buttons...@buttons[0].clicked.connectdefspam():...@buttons[1].clicked.connectdefeggs():...# Do stuff with the list of buttons...
Currently, these decorations must be rewritten as something like:
button_0=buttons[0]@button_0.clicked.connectdefspam():...button_1=buttons[1]@button_1.clicked.connectdefeggs():...
Further, the current grammar is already loose enough that it’s trivialto hack more complicated decorator expressions together. So ratherthan disallow arbitrarily complex expressions, as intended, thecurrent restrictions only make them uglier and less efficient:
# Identity function hack:def_(x):returnx@_(buttons[0].clicked.connect)defspam():...# eval hack:@eval("buttons[1].clicked.connect")defeggs():...
The decision to allowany valid expression (and not just relaxingthe current restrictions to allow, for example, subscripting) hasbeen considered as the next logical step in the evolution of decoratorgrammar for quite some time. AsGuido noted,during yet another mailing list thread:
I don’t think it’s reasonable to constrain it less than itcurrently is but more than a general expression.
Special-casing the grammar to allowsome useful cases would onlycomplicate the current situation, and all but guarantee that theprocess would repeat itself sometime in the future. Further, onepurpose of this grammatical change is to discourage the temptation touse hacks like theeval and identity-function anti-patterns shownabove.
In short: if we’re removing somewhat arbitrary restrictions, we shouldremoveall of them.
Throughout this document, the word “expression” is used as defined inthePython Language Reference.This can be summarized as “anything that’s valid as a test inif,elif, andwhile blocks”. This differs subtly from a perhapsmore populardefinition, which canbe summarized as “anything that’s valid as string input toeval”.
This definition of “expression” is convenient in that it fits ourneeds well, and reuses the allowed grammar of existing languageconstructs. It has two subtle differences from the other definition:
This is based on an observation Guido made in the same email.Continued immediately from above:
Though I wouldn’t allow commas– there’s no way that@f,gdefpooh():...can make sense.
Indeed, it may even lead inexperienced readers to conclude thatseveral decorators are being applied, as if they were stacked.Requiring parentheses here makes the (admittedly nonsensical) intentclear without imposing further restrictions and grammar complications.
Here, the choice of syntax is unambiguous.PEP 572 explainswhy it requires parentheses around top-level expression statements:
This rule is included to simplify the choice for the user betweenan assignment statement and an assignment expression – there isno syntactic position where both are valid.
Since an assignment statement is not valid here, assignmentexpressions should not be unnecessarily burdened with parentheses.
The grammar for decorators is currently:
decorator:'@'dotted_name['('[arglist]')']NEWLINE
This PEP proposes that it be simplified to:
decorator:'@'namedexpr_testNEWLINE
This new grammar is fully backward-compatible with the existinggrammar.
Decorators can continue to be taught as they always have; the averagePython programmer is likely unaware that the current restriction evenexists.
The author has written aCPython implementation.
This document is placed in the public domain or under theCC0-1.0-Universal license, whichever is more permissive.
Source:https://github.com/python/peps/blob/main/peps/pep-0614.rst
Last modified:2025-02-01 08:59:27 GMT