This PEP proposes to introduce syntactic sugarf(x=) for the common patternwhere a keyword argument has the same name as that of the variable correspondingto its valuef(x=x).
Keyword argument syntax can become needlessly repetitive and verbose.
Consider the following call:
my_function(my_first_variable=my_first_variable,my_second_variable=my_second_variable,my_third_variable=my_third_variable,)
The case of a keyword argument name matching the variable name of its value isprevalent among Python libraries. This redundancy discourages use of namedarguments and reduces readability by increasing visual noise.
There are two ways to invoke a function with arguments: by position and bykeyword. By being explicit, keyword arguments increase readability andminimise the risk of inadvertent transposition. On the flipside, positionalarguments are often preferred simply to minimise verbosity and visual noise.
We contend that a simple syntactic sugar used to simplify this common patternwould confer numerous benefits:
By reducing the visual noise that established keyword argument syntax can cause,this syntax would encourage the use of named arguments, thereby increasingreadability and reducing bugs from argument transposition.
By minimising visual noise and in some cases lines of code, we can increasereadability.
A common problem is that semantically identical variables have different namesdepending on their contexts. This syntax would encourage authors to use the samevariable name when calling a function as the argument name, which would increaseconsistency of variable names used and hence improve readability.
With the current syntax, function calls where many arguments are forwarded fromthe local context can make other argument values easy to miss due to the visualnoise. For example:
add_middleware(excluded_urls=excluded_urls,server_request=server_request,client_request=client_request,client_response=client_response,span_details=_get_span_details(),tracer=tracer,meter=meter,)
With this syntax, the exceptional arguments become easier to identify:
add_middleware(excluded_urls=,server_request=,client_request=,client_response=,span_details=_get_span_details(),tracer=,meter=,)
This syntax can be applied to dictionary construction where a similar patternfrequently occurs (where dictionary keys are identical the names of thevariables assigned as their values),{"x":x,"y":y} ordict(x=x,y=y).With this feature, this can now also be trivially written asdict(x=,y=).Whether to further support similar syntax in dictionary literals is an openquestion beyond the scope of this PEP.
We propose to introduce syntactic sugar such that, if the value of a keywordargument is omitted from a function invocation, the argument’s value is inferredto be the variable matching that name at the invocation scope.
For example, the function invocation:
my_function(my_first_variable=,my_second_variable=,my_third_variable=)
Will be interpreted exactly equivalently to following in existing syntax:
my_function(my_first_variable=my_first_variable,my_second_variable=my_second_variable,my_third_variable=my_third_variable,)
If no variable matches that name in the invocation scope, aNameError israised in an identical manner as would be with the established expanded syntax.
This proposal only pertains to function invocations; function definitions areunaffected by the syntax change. All existing valid syntax is unchanged.
Only new syntax is added which was previously syntactically erroneous. Noexisting valid syntax is modified. As such, the changes proposed are fullybackwards compatible.
There are no security implications for this change.
Python already possesses a very similar feature in f-string interpolation wheref'{x=}' is effectively expanded tof'x={x}' (seerelated GitHub issue).
Several modern languages provide similar features during function invocation,sometimes referred to as ‘punning’. For example:
f(x:,y:) is syntactic sugar forf(x:x,y:y). See theRuby 3.1.0 release notes (search for “keyword arguments”).f(~x,~y) is syntactic sugar forf(~x=x,~y=y). See theReasonML function documentation (search for “punning”).(.mult,.mop1,.data); is syntactic sugar for(.mult(mult),.mop1(mop1), .data(data));. SeeSystemVerilog Implicit Port Connections.f(x,y) is syntactic sugar forf(x:x,y:y). SeeThe Jakt programming language.Beyond function invocation specifically, more languages offer similar features:
let+xin… is syntactic sugar forlet+x=xin…. SeeOCaml: Short notation for variable bindings (let-punning).{x,y} is syntactic sugar for{x:x,y:y}. See#"https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-the-field-init-shorthand-when-variables-and-fields-have-the-same-name">Rust: Using the Field Init Shorthand.We analysed popular Python libraries from the last few years usingthis scriptto compute:
f(x=x) atinvocation.f(x=x) atinvocation.The purpose of this exercise was to compute statistics about the prevalence ofthis pattern and should not be interpreted as a recommendation that the proposedsyntactic sugar should be applied universally.
| Statistic | Polars | FastAPI | Rich | HTTPX |
|---|---|---|---|---|
Number of keyword arguments of the formf(x=x) at invocation | 1,654 | 1,408 | 566 | 759 |
Percentage of keyword arguments of the formf(x=x) at invocation | 15.83% | 28.11% | 15.74% | 45.13% |
| Lines saved | 170 | 35 | 62 | 117 |
Based on this, we note that thef(x=x) keyword argument pattern iswidespread, accounting for anywhere from 15% to just below half of all keywordargument uses depending on the codebase.
While this feature has been proposed on numerous occasions with severaldifferent forms[1][2][3][4][5],[6] we have opted to advocatefor thef(x=) form for the following reasons:
f(x=) orf(=x) being by far the most commonly suggested syntax[1][2][6]. This strongly indicates that it is the most obvious notation.f'{var=}' syntax(established Pythonic style) and serves an almost identical purpose.To ease the communication of and search for this feature, it may also bevaluable to provide this feature with a name, such as ‘keyword argumentshorthand’.
Keen Python developers will likely hear about this feature through typicalinformation channels, such as newsboards, social media, mailing lists, onlineforums, or word of mouth. Many more will encounter this feature while readingcode and noting the omission of the value in a keyword argument at invocation,violating their expectations. We should ensure such developers have easy accessto documentation that explains the semantics of this feature and that thisdocumentation is easy to find when searching. For example, thePython Glossary andTutorialmay be updated accordingly and reasonable keywords may be used to help withsearch discoverability.A StackOverflow questioncould be written to help explain this feature to those searching for anexplanation.
A teacher may explain this feature to new Python programmers as, “where you seean argument followed only by an equals sign, such asf(x=), this representsa keyword argument where the name of the argument and its value are the same.This can be written equivalently in the expanded notation,f(x=x).”Depending on a student’s background, a teacher might further compare this toequivalent syntax in other languages or to Python’s f-string syntaxf"{x=}".
To understand this, a student of Python would need to be familiar with thebasics of functions in addition to the existing keyword argument syntax.Given that this feature is a relatively straightforward syntactic sugar, it isreasonable that a student who possesses a grasp of keyword arguments will beable to absorb this concept quickly. This is evidenced by the success of thef-string syntax as well as similar features in other languages (seePrior Art).
Many alternative syntaxes have been proposed however no form other thanf(=x) orf(x=) has garnered significant support. We here enumerate someof the most popular proposed alternatives and why we ultimately reject them.
f(a,b,*,x)On a few occasions the idea has been floated to borrow the syntax fromkeyword-only function definitions.
In favour of this proposal:
However, we object that:
* could easily be missed in a long argument listand named arguments may be read as positional or vice versa.*. If so, then their relative position will be confusinglyarbitrary, but if not, then an arbitrary grouping is enforced betweendifferent types of keyword arguments and reordering of arguments would benecessary if only one name (the argument or its value) was changed.* in function calls is well established and this proposal wouldintroduce a new effect which could cause confusion. For example,f(a,*x,y) would mean something different thanf(a,*,x,y).f(=x)In favour of this form:
*args and**kwargs syntax for function calls.On the contrary:
= it is clear that the value is filled inautomatically just as the value is clear in the typical keyword argument case.f(=a+b), since such expressions are acceptable after the equals sign inthe current keyword argument syntax but not before it.f(%x) orf(:x) orf(.x)Several flavours of this syntax have been proposed with the prefix formsubstituting another character for=. However, no such form has gainedtraction and the choice of symbol seems arbitrary compared to=.Additionally, there is less precedent in terms of existing language features(such as f-string) or other languages (such as Ruby).
There are only a few hard objections to the introduction of this syntacticsugar. Most of those not in favour of this feature are in the camp of ‘Iwouldn’t use it’. However, over the extensive conversations about this feature,the following objections were the most common:
This objection is the most common. On the contrary, we argue that:
We argue that:
f'{x=}' syntax.x= tox=x is a trivial feature and inherentlysignificantly less complex than the popular*arg and**kwargexpansions.We recognise that, in an obvious sense, the argument value is ‘implicit’ in thisproposed syntax. However, we do not think that this is what the Zen of Python isaiming to discourage.
In the sense that we take the Zen to be referring to, keyword arguments (forexample) are more explicit than positional arguments where the argument name isomitted and impossible to tell from the local context. Conversely, the syntacticsugar for integersx+=1 is not more implicit thanx=x+1 in thissense, even though the variable is omitted from the right hand side, because itis immediately obvious from the local context what it is.
The syntax proposed in this PEP is much more closely analogous to thex+=1example (although simpler since we do not propose to introduce a new operation).Moreover, by removing the barrier of visual noise introduced by the existingkeyword argument syntax, this syntactic sugar will encourage the use of keywordarguments over positional ones, making typical Python codebases more explicit ingeneral.
The same argument can be made against all syntax changes. This is a simplesyntactic sugar, much asx+=1 is sugar forx=x+1 whenx is aninteger. This isn’t tantamount to a ‘new way’ of passing arguments but a morereadable notation for the same way.
ANameError would make the mistake clear in the large majority cases. Theremay be confusion if a variable from a broader scope has the same name as theoriginal variable, so noNameError would be raised. However, this issue canalso occur with keyword arguments using the current syntax (although arguably,this syntactic sugar could make it harder to spot). Moreover, having variableswith the same name in different scopes is broadly considered to be bad practiceand is discouraged by linters.
Code editors could highlight the issue based on static analysis –f(x=) isexactly equivalent to writingf(x=x). Ifx does not exist, moderneditors have no problem highlighting the issue.
We recognise that, as ever, all syntax has the potential for misuse and soshould be applied judiciously to improve codebases. In this case, if a parameterand its value have the same semantics in both contexts, that suggests that usingthis syntax is appropriate and will help ameliorate the risk of unintentionaldesynchronisation which harms readability.
However, if the two variables have different semantics, that suggests that thisfeature should not be used (since it encourages consistency) or perhaps that oneor both of the variables should be renamed.
As with any other language feature, the programmer should exercise their ownjudgement about whether it is prudent to use it in any given context. We do notrecommend enforcing a rule to use the feature in all cases where it may beapplicable, such as via lint rules or style guides.
As described inThis syntax increases coupling, we propose that a reasonablerule of thumb would be to use this in cases where a parameter and its argumenthave the same semantics in order to reduce unintentional desynchronisationwithout causing inappropriate coupling.
Editing with a plain text editor should generally be unaffected.
When renaming a variable using a ‘Find-Replace’ method, where this syntax isused the developer will come across the function argument at invocation (as theywould if this syntax was not used). At that point, they can, as usual, decidewhether to update the argument as well or expand to the fullf(x=x) syntax.
As with the current syntax, a ‘Find-Replace All’ method would fail since thekeyword argument would not exist at function definition, in the vast majorityof cases.
If the developer leaves the argument name unchanged and forgets to update itsvalue, aNameError will typically be raised as described inRenaming the variable in the calling context will break the code.
In response to community feedback, we include some suggestions regarding howIDEs could handle this syntax. However, we defer to the domain expertsdeveloping IDEs to use their discretion.
Most considerations are made simple by recognising thatf(x=) is justsyntactic sugar forf(x=x) and should be treated the same as at present.
IDEs typically offer a feature to highlight code that may cause aNameError.We recommend that this syntax be treated similarly to the expanded formf(x=x) to identify and highlight cases where the elided variable may notexist. What visual cue may be used to highlight these cases may be the same ordifferent from that which would be used with the current syntax, depending onthe IDE.
There are a few possible ways that a ‘jump to definition’ feature could beimplemented depending on the caret/cursor position.
One option is to:
= in our proposed syntaxAnother, potentially complementary, option would be to expand the syntaxvisually on mouseover and enable aCtrl+Click (orCmd+Click) to thedefinition of the variable.
IDEs frequently highlight matching code references to the value at the currentcaret/cursor position. With this shorthand syntax, when the caret/cursor is onthe argument name it may be valuable to either:
There are a few ways that IDEs may wish to support a ‘Rename symbol’ feature forthis syntax. For example, if the argument is being renamed, the IDE may:
The last option seems to be the most preferable to reduce unintentionaldesynchronisation of names while highlighting the changes to the programmer.
A proposed implementationfor CPython has been provided by @Hels15. We will extend this implementation toadd an AST node attribute indicating for keywords whether the value was elided.Otherwise the AST will remain unchanged.
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-0736.rst
Last modified:2025-04-14 02:39:58 GMT