Rejected by the Steering Council:https://mail.python.org/archives/list/python-dev@python.org/message/SQC2FTLFV5A7DV7RCEAR2I2IKJKGK7W3/
This PEP proposes new syntax forunused variables, providing a pseudo-namethat can be assigned to but not otherwise used. The assignment doesn’tactually happen, and the value is discarded instead.
In Python it is somewhat common to need to do an assignment without actuallyneeding the result. Conventionally, people use either"_" or a name suchas"unused" (or with"unused" as a prefix) for this. It’s mostcommon inunpacking assignments:
x,unused,z=range(3)x,*unused,z=range(10)
It’s also used infor loops and comprehensions:
forunusedinrange(10):...[SpamObject()forunusedinrange(10)]
The use of"_" in these cases is probably the most common, but itpotentially conflicts with the use of"_" in internationalization, wherea call like gettext.gettext() is bound to"_" and used to mark stringsfor translation.
In the proposal to add Pattern Matching to Python (originallyPEP 622, nowsplit intoPEP 634,PEP 635 andPEP 636),"_" has anadditionalspecial meaning. It is a wildcard pattern, used in places where variablescould be assigned to, to indicate anything should be matched but notassigned to anything. The choice of"_" there matches the use of"_"in other languages, but the semantic difference with"_" elsewhere inPython is significant.
This PEP proposes to allow a special token,"?", to be used instead ofany valid name in assignment. This has most of the benefits of"_"without affecting other uses of that otherwise regular variable. Allowingthe use of the same wildcard pattern would make pattern matching andunpacking assignment more consistent with each other.
Marking certain variables as unused is a useful tool, as it helps clarity ofpurpose of the code. It makes it obvious to readers of the code as well asautomated linters, that a particular variable isintentionally unused.
However, despite the convention,"_" is not a special variable. Thevalue is still assigned to, the object it refers to is still kept aliveuntil the end of the scope, and it can still be used. Nor is the use of"_" for unused variables entirely ubiquitous, since it conflicts withconventional internationalization, it isn’t obvious that it is a regularvariable, and it isn’t as obviously unused like a variable named"unused".
In the Pattern Matching proposal, the use of"_" for wildcard patternsside-steps the problems of"_" for unused variables by virtue of itbeing in a separate scope. The only conflict it has withinternationalization is one of potential confusion, it will not actuallyinteract with uses of a global variable called"_". However, thespecial-casing of"_" for this wildcard pattern purpose is stillproblematic: the different semanticsand meaning of"_" inside patternmatching and outside of it means a break in consistency in Python.
Introducing"?" as special syntax for unused variablesboth inside andoutside pattern matching allows us to retain that consistency. It avoidsthe conflict with internationalizationor any other uses of _ as avariable. It makes unpacking assignment align more closely with patternmatching, making it easier to explain pattern matching as an extension ofunpacking assignment.
In terms of code readability, using a special token makes it easier to findout what it means ("whatdoesquestionmarkinPythondo" versus"whyismy_variablenotgettingassignedto"), and makes it more obvious thatthe actual intent is for the value to be unused – since it is entirelyimpossible to use it.
A new token is introduced,"?", ortoken.QMARK.
The grammar is modified to allow"?" in assignment contexts(star_atom andt_atom in the current grammar), creating aNameAST node with identifier set to NULL.
The AST is modified to allow theName expression’s identifier to beoptional (it is currently required). The identifier being empty would onlybe allowed in aSTORE context.
In CPython, the bytecode compiler is modified to emitPOP_TOP instead ofSTORE_NAME forName nodes with no identifier. Other uses of theName node are updated to handle the identifier being empty, asappropriate.
The uses of the modified grammar nodes encompass at least the followingforms of assignment:
? = ...x, ?, z = ...x, *?, z = ...for ? in range(3): ... # including comprehension formsfor x, ?, z in matrix: ... # including comprehension formswith open(f) as ?: ...with func() as (x, ?, z): ...
The use of a single"?", not in an unpacking context, is allowed innormal assignment and thewith statement. It doesn’t really make senseon its own, and it is possible to disallow those specific cases. However,for?inrange(3) clearly has its uses, so for consistency reasons ifnothing else it seems more sensible to allow the use of the single"?"in other cases.
Using"?" in augmented assignment (?*=2) is not allowed, since"?" can only be used for assignment. Having multiple occurrences of"?" is valid, just like when assigning to names, and the assignments donot interfere with each other.
Introducing a new token means there are no backward compatibility concerns.No valid syntax changes meaning.
"?" is not considered an identifier, sostr.isidentifier() does notchange.
The AST does change in an incompatible way, as the identifier of a Nametoken can now be empty. Code using the AST will have to be adjustedaccordingly.
"?" can be introduced along with unpacking assignment, explaining it isspecial syntax for ‘unused’ and mentioning that it can also be used in otherplaces. Alternatively, it could be introduced as part of an explanation onassignment infor loops, showing an example where the loop variable isunused.
PEP 636 discusses how to teach"_", and can simply replace"_" with"?", perhaps noting that"?" is similarly usable in other contexts.
A prototype implementation exists at<https://github.com/Yhg1s/cpython/tree/nonassign>.
Should"?" be allowed in the following contexts:
# imports done for side-effect only.import os as ?from os import path as ?# Function defined for side-effects only (e.g. decorators)@register_my_funcdef ?(...): ...# Class defined for side-effects only (e.g. decorators, __init_subclass__)class ?(...): ...# Parameters defined for unused positional-only arguments:def f(a, ?, ?): ...lambda a, ?, ?: ...# Unused variables with type annotations:?: int = f()# Exception handling:try: ...except Exception as ?: ...# With blocks:with open(f) as ?: ...
Some of these may seem to make sense from a consistency point of view, butpractical uses are limited and dubious. Type annotations on"?" andusing it withexcept andwith do not seem to make any sense. In thereference implementation,except is not supported (the existing syntaxonly allows a name) butwith is (by virtue of the existing syntaxsupporting unpacking assignment).
Should this PEP be accepted even if pattern matching is rejected?
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-0640.rst
Last modified:2025-02-01 08:55:40 GMT