PEP 671: Quick pass over wording and settle some things from discussion

This commit is contained in:
Chris Angelico 2021-12-01 17:04:57 +11:00
parent 745bfda492
commit 45cf9e5250
1 changed files with 31 additions and 22 deletions

View File

@ -80,29 +80,27 @@ implementations, it should be considered dubious::
def spaminate(sausage=>eggs + 1, eggs=>sausage - 1): # Confusing, may fail def spaminate(sausage=>eggs + 1, eggs=>sausage - 1): # Confusing, may fail
def frob(n=>len(items), items=[]): # May fail, may succeed def frob(n=>len(items), items=[]): # May fail, may succeed
Moreover, even if syntactically and semantically legal, this kind of construct
is highly confusing to other programmers, and should be avoided.
Choice of spelling Choice of spelling
------------------ ------------------
Our chief syntax proposal is ``name=>expression`` -- our two syntax proposals While this document specifies a single syntax ``name=>expression``, alternate
... ahem. Amongst our potential syntaxes are:: spellings are similarly plausible. Open for consideration are the following:
# Preferred options: adorn the equals sign (approximate preference order)
def bisect(a, hi=>len(a)): def bisect(a, hi=>len(a)):
def bisect(a, hi=:len(a)):
def bisect(a, hi:=len(a)): def bisect(a, hi:=len(a)):
def bisect(a, hi?=len(a)): def bisect(a, hi?=len(a)):
def bisect(a, hi!=len(a)):
def bisect(a, hi=\len(a)): An alternative reference implementation is under consideration, which would
def bisect(a, hi=@len(a)): use this syntax::
# Less preferred option: adorn the variable name
def bisect(a, @hi=len(a)): def bisect(a, @hi=len(a)):
# Less preferred option: adorn the expression
def bisect(a, hi=`len(a)`):
Since default arguments behave largely the same whether they're early or late Since default arguments behave largely the same whether they're early or late
bound, the preferred syntax is very similar to the existing early-bind syntax. bound, the syntax is deliberately similar to the existing early-bind syntax.
The alternatives offer little advantage over the preferred one.
How to Teach This How to Teach This
================= =================
@ -117,6 +115,12 @@ bound arguments are broadly equivalent to code at the top of the function::
def add_item(item, target=<OPTIONAL>): def add_item(item, target=<OPTIONAL>):
if target was omitted: target = [] if target was omitted: target = []
A simple rule of thumb is: "target=expression" is evaluated when the function
is defined, and "target=>expression" is evaluated when the function is called.
Either way, if the argument is provided at call time, the default is ignored.
While this does not completely explain all the subtleties, it is sufficient to
cover the important distinction here (and the fact that they are similar).
Interaction with other open PEPs Interaction with other open PEPs
================================ ================================
@ -132,13 +136,15 @@ be defined by the function. Additionally, dedicated sentinel objects can be
used as dictionary lookup keys, where PEP 671 does not apply. used as dictionary lookup keys, where PEP 671 does not apply.
Open Issues Interaction with annotations
=========== ============================
- Annotations go before the default, so in all syntax options, it must be Annotations go before the default, so in all syntax options, it must be
unambiguous (both to the human and the parser) whether this is an annotation, unambiguous (both to the human and the parser) whether this is an annotation,
a default, or both. The worst offender is the ``:=`` notation, as ``:int=`` a default, or both. The alternate syntax ``target:=expr`` runs the risk of
would be a valid annotation and early-bound default. being misinterpreted as ``target:int=expr`` with the annotation omitted in
error, and may thus mask bugs. The preferred syntax ``target=>expr`` does not
have this problem.
Implementation details Implementation details
@ -159,10 +165,13 @@ needs to be queried. If it is ``None``, then the default is indeed the value
``Ellipsis``; otherwise, it is a descriptive string and the true value is ``Ellipsis``; otherwise, it is a descriptive string and the true value is
calculated as the function begins. calculated as the function begins.
When a parameter with a late-bound default is omitted, the When a parameter with a late-bound default is omitted, the function will begin
function will begin with the parameter unbound. The function begins by testing with the parameter unbound. The function begins by testing for each parameter
for each parameter with a late-bound default, and if unbound, evaluates the with a late-bound default using a new opcode QUERY_FAST, and if unbound,
original expression. evaluates the original expression. This opcode (available only for fast locals
and not for other types of variable) pushes True onto the stack if the given
local has a value, and False if not - meaning that it pushes False if LOAD_FAST
would raise UnboundLocalError and True if it would succeed.
Out-of-order variable references are permitted as long as the referent has a Out-of-order variable references are permitted as long as the referent has a
value from an argument or early-bound default. value from an argument or early-bound default.