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 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
------------------
Our chief syntax proposal is ``name=>expression`` -- our two syntax proposals
... ahem. Amongst our potential syntaxes are::
While this document specifies a single syntax ``name=>expression``, alternate
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)):
# Less preferred option: adorn the variable name
An alternative reference implementation is under consideration, which would
use this syntax::
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
bound, the preferred syntax is very similar to the existing early-bind syntax.
The alternatives offer little advantage over the preferred one.
bound, the syntax is deliberately similar to the existing early-bind syntax.
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>):
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
================================
@ -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.
Open Issues
===========
Interaction with annotations
============================
- 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,
a default, or both. The worst offender is the ``:=`` notation, as ``:int=``
would be a valid annotation and early-bound default.
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,
a default, or both. The alternate syntax ``target:=expr`` runs the risk of
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
@ -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
calculated as the function begins.
When a parameter with a late-bound default is omitted, the
function will begin with the parameter unbound. The function begins by testing
for each parameter with a late-bound default, and if unbound, evaluates the
original expression.
When a parameter with a late-bound default is omitted, the function will begin
with the parameter unbound. The function begins by testing for each parameter
with a late-bound default using a new opcode QUERY_FAST, and if unbound,
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
value from an argument or early-bound default.