(?it=expr)
is a new atomic expression for an "it reference binding"- subsequent subexpressions (in execution order) can reference the bound subexpression using
?it
(an "it reference") ?it
is reset between statements, including before entering the suite within a compound statement (if you want a persistent binding, use a named variable)- for conditionals, put the reference binding in the conditional, as that gets executed first
- to avoid ambiguity, especially in function calls (where it could be confused with keyword argument syntax), the parentheses around reference bindings are always required
None
-aware attribute access:
value = ?it.strip()[4:].upper() if (?it=var1) is not None else None
None
-aware subscript access:
value = ?it[4:].upper() if (?it=var1) is not None else None
None
-coalescense:
value = ?it if (?it=var1) is not None else ?it if (?it=var2) is not None else var3
NaN
-coalescence:
value = ?it if not math.isnan((?it=var1)) else ?it if not math.isnan((?that=var2)) else var3
Conditional function call:
value = ?it() if (?it=calculate) is not None else default
Avoiding repeated evaluation of a comprehension filter condition:
filtered_values = [?it for x in keys if (?it=get_value(x)) is not None]
Avoiding repeated evaluation for range and slice bounds:
range((?it=calculate_start()), ?it+10)
data[(?it=calculate_start()):?it+10]
Avoiding repeated evaluation in chained comparisons:
value if (?it=lower_bound()) <= value < ?it+tolerance else 0
Avoiding repeated evaluation in an f-string:
print(f"{?it=get_value()!r} is printed in pure ASCII as {?it!a} and in Unicode as {?it}"
A possible future extension would then be to pursue PEP 3150, treating the nested namespace as an it reference binding, giving:
sorted_data = sorted(data, key=?it.sort_key) given ?it=:
def sort_key(item):
return item.attr1, item.attr2
(A potential added bonus of that spelling is that it may be possible to make "given ?it=:" the syntactic keyword introducing the suite, allowing "given" itself to continue to be used as a variable name)
(?=expr)
(binding) and?
(reference): cryptic to read, no obvious pronunciation(?expr)
(binding) and??
(reference): cryptic to read, no obvious pronunciation(that=expr)
(binding) andthat
(reference): looks too much like function call keyword arguments and ordinary variable references(?that=expr)
(binding) and?that
(reference):that
was the first pronoun considered, but the proposal switched toit
to make the boilerplate lighter
(Using this as a convenient place to record a few key concerns with PEP 505)
During the pre-3.7-beta discussions, Serhiy noted that all of the languages that include a "??" operator use "c ? a : b" as the syntax for their conditional expressions, making "c ?? b" a more natural shorthand for "c ? c : a".
After coming up with this alternative proposal, I also realised those languages share another characteristic: their
or
operator equivalents (typically||
) are defined as returning a boolean result, rather than as propagating the type of the operands.Python is different: our
or
already propagates the type of the operands, and you have to wrap it inbool
(either implicitly or explicitly) to coerce it to a boolean value.The other big difference is that the languages with
??
by and large don't treat empty containers as being false: they either don't allow containers to be implicitly coerced to boolean values at all, or else they treat them as opaque references (where anil
orNULL
is false, but even an empty container is true). (_Note: I need to double check this and make sure it's actually true for all the languages mentioned in the PEP)This meant that PEP 505 ended up using the
or
vs??
distinction to try to define a second form of implicit coercion tobool
(one based onobj is not None
rather thanbool(obj)
), leading to the extra complexity of PEP 532's "circuit breaking protocol" as a way to make that less arbitrary.