If recommend 'valued(foo)'. Without the final 'd' I think of "the value of foo" rather than "does foo have a value?" Obviously, the value of foo is just spelled 'foo' in Python, but it seems confusing.
'exists(foo)' is even more confusing since almost everyone will read it as "is foo defined?" I know you can't do that with a call in Python, but you can in lots of other languages. On Nov 12, 2016 8:50 AM, "Mark E. Haase" <meha...@gmail.com> wrote: I like PEP-532. Given the opposition to non-Pythonic syntax like ?? (i.e. PEP-505), Nick's proposal offers a Pythonic alternative that is protocol based, more generalized, and uses built-ins and keywords to avoid punctuation. I agree with other posters that the terms "exists" and "missing" could lead developers to think that it tests for NameError. Maybe "value(foo) else bar"? I can't think of a better spelling for the inverse. Maybe the "if" syntax described in the PEP is better: "foo.bar if value(foo)". In that case, we wouldn't need an inverse to exists()/value()/whatever. I also wanted to mention a couple of unmentioned benefits of this PEP: 1. It is easier to Google a name. E.g., Google "c# ??" and you'll get nothing related to null coalescing in c#". ("C# question marks" does find the right content, however.) 2. Dismay over the meaning of foo?.bar.baz is much clearer when expressed as missing(foo) else foo.bar.baz -- it's very close to the ternary logic you'd write if you didn't have a circuit breaking operator: None if foo is None else foo.bar.baz. On Sat, Nov 5, 2016 at 5:50 AM, Nick Coghlan <ncogh...@gmail.com> wrote: > Hi folks, > > As promised, here's a follow-up to the withdrawn PEP 531 that focuses > on coming up with a common protocol driven solution to conditional > evaluation of subexpressions that also addresses the element-wise > comparison chaining problem that Guido noted when rejecting PEP 335. > > I quite like how it came out, but see the "Risks & Concerns" section > for a discussion of an internal inconsistency it would introduce into > the language if accepted as currently written, and the potentially > far-reaching consequences actually resolving that inconsistency might > have on the way people write their Python code (if the PEP was > subsequently approved). > > Regards, > Nick. > > ================================= > PEP: 532 > Title: A circuit breaking operator and protocol > Version: $Revision$ > Last-Modified: $Date$ > Author: Nick Coghlan <ncogh...@gmail.com> > Status: Draft > Type: Standards Track > Content-Type: text/x-rst > Created: 30-Oct-2016 > Python-Version: 3.7 > Post-History: 5-Nov-2016 > > Abstract > ======== > > Inspired by PEP 335, PEP 505, PEP 531, and the related discussions, this > PEP > proposes the addition of a new protocol-driven circuit breaking operator to > Python that allows the left operand to decide whether or not the expression > should short circuit and return a result immediately, or else continue > on with evaluation of the right operand:: > > exists(foo) else bar > missing(foo) else foo.bar() > > These two expressions can be read as: > > * "the expression result is 'foo' if it exists, otherwise it is 'bar'" > * "the expression result is 'foo' if it is missing, otherwise it is > 'foo.bar()'" > > Execution of these expressions relies on a new circuit breaking protocol > that > implicitly avoids repeated evaluation of the left operand while letting > that operand fully control the result of the expression, regardless of > whether > it skips evaluation of the right operand or not:: > > _lhs = LHS > type(_lhs).__then__(_lhs) if _lhs else type(_lhs).__else__(_lhs, RHS) > > To properly support logical negation of circuit breakers, a new ``__not__`` > protocol method would also be introduced allowing objects to control > the result of ``not obj`` expressions. > > As shown in the basic example above, the PEP further proposes the addition > of > builtin ``exists`` and ``missing`` circuit breakers that provide > conditional > branching based on whether or not an object is ``None``, but return the > original object rather than the existence checking wrapper when the > expression > evaluation short circuits. > > In addition to being usable as simple boolean operators (e.g. as in > ``assert all(exists, items)`` or ``if any(missing, items):``), these > circuit > breakers will allow existence checking fallback operations (aka > None-coalescing > operations) to be written as:: > > value = exists(expr1) else exists(expr2) else expr3 > > and existence checking precondition operations (aka None-propagating > or None-severing operations) to be written as:: > > value = missing(obj) else obj.field.of.interest > value = missing(obj) else obj["field"]["of"]["interest"] > > A change to the definition of chained comparisons is also proposed, where > the comparison chaining will be updated to use the circuit breaking > operator > rather than the logical disjunction (``and``) operator if the left hand > comparison returns a circuit breaker as its result. > > While there are some practical complexities arising from the current > handling > of single-valued arrays in NumPy, this change should be sufficient to allow > elementwise chained comparison operations for matrices, where the result > is a matrix of boolean values, rather than tautologically returning > ``True`` > or raising ``ValueError``. > > > Relationship with other PEPs > ============================ > > This PEP is a direct successor to PEP 531, replacing the existence checking > protocol and the new ``?then`` and ``?else`` syntactic operators defined > there > with a single protocol driven ``else`` operator and adjustments to the > ``not`` > operator. The existence checking use cases are taken from that PEP. > > It is also a direct successor to PEP 335, which proposed the ability to > overload the ``and`` and ``or`` operators directly, with the ability to > overload the semantics of comparison chaining being one of the consequences > of that change. The proposal in this PEP to instead handle the element-wise > comparison use case by changing the semantic definition of comparison > chaining > is drawn from Guido's rejection of PEP 335. > > This PEP competes with the dedicated null-coalescing operator in PEP 505, > proposing that improved support for null-coalescing operations be offered > through a more general protocol-driven short circuiting operator and > related > builtins, rather than through a dedicated single-purpose operator. > > It doesn't compete with PEP 505's proposed shorthands for existence > checking > attribute access and subscripting, but instead offers an alternative > underlying > semantic framework for defining them: > > * ``EXPR?.attr`` would be syntactic sugar for ``missing(EXPR) else > EXPR.attr`` > * ``EXPR?[key]`` would be syntactic sugar for ``missing(EXPR) else > EXPR[key]`` > > In both cases, the dedicated syntactic form could be optimised to avoid > actually creating the circuit breaker instance. > > > Specification > ============= > > The circuit breaking operator (``else``) > ---------------------------------------- > > Circuit breaking expressions would be written using ``else`` as a new > binary > operator, akin to the existing ``and`` and ``or`` logical operators:: > > LHS else RHS > > Ignoring the hidden variable assignment, this is semantically equivalent > to:: > > _lhs = LHS > type(_lhs).__then__(_lhs) if _lhs else type(_lhs).__else__(_lhs, RHS) > > The key difference relative to the existing ``or`` operator is that the > value > determining which branch of the conditional expression gets executed *also* > gets a chance to postprocess the results of the expressions on each of the > branches. > > As part of the short-circuiting behaviour, interpreter implementations > are expected to access only the protocol method needed for the branch > that is actually executed, but it is still recommended that circuit > breaker authors that always return ``True`` or always return ``False`` from > ``__bool__`` explicitly raise ``NotImplementedError`` with a suitable > message from branch methods that are never expected to be executed (see the > comparison chaining use case in the Rationale section below for an example > of that). > > It is proposed that the ``else`` operator use a new precedence level that > binds > less tightly than the ``or`` operator by adjusting the relevant line in > Python's grammar from the current:: > > test: or_test ['if' or_test 'else' test] | lambdef > > to instead be:: > > test: else_test ['if' or_test 'else' test] | lambdef > else_test: or_test ['else' test] > > The definition of ``test_nocond`` would remain unchanged, so circuit > breaking expressions would require parentheses when used in the ``if`` > clause of comprehensions and generator expressions just as conditional > expressions themselves do. > > This grammar definition means precedence/associativity in the otherwise > ambiguous case of ``expr1 if cond else expr2 else epxr3`` resolves as > ``(expr1 if cond else expr2) else epxr3``. > > A guideline will also be added to PEP 8 to say "don't do that", as such a > construct will be inherently confusing for readers, regardless of how the > interpreter executes it. > > > Overloading logical inversion (``not``) > --------------------------------------- > > Any circuit breaker definition will have a logical inverse that is still a > circuit breaker, but inverts the answer as to whether or not to short > circuit > the expression evaluation. For example, the ``exists`` and ``missing`` > circuit > breakers proposed in this PEP are each other's logical inverse. > > A new protocol method, ``__not__(self)``, will be introduced to permit > circuit > breakers and other types to override ``not`` expressions to return their > logical inverse rather than a coerced boolean result. > > To preserve the semantics of existing language optimisations, ``__not__`` > implementations will be obliged to respect the following invariant:: > > assert not bool(obj) == bool(not obj) > > > Chained comparisons > ------------------- > > A chained comparison like ``0 < x < 10`` written as:: > > LEFT_BOUND left_op EXPR right_op RIGHT_BOUND > > is currently roughly semantically equivalent to:: > > _expr = EXPR > _lhs_result = LEFT_BOUND left_op _expr > _expr_result = _lhs_result and (_expr right_op RIGHT_BOUND) > > This PEP proposes that this be changed to explicitly check if the left > comparison returns a circuit breaker, and if so, use ``else`` rather than > ``and`` to implement the comparison chaining:: > > _expr = EXPR > _lhs_result = LEFT_BOUND left_op _expr > if hasattr(type(_lhs_result), "__then__"): > _expr_result = _lhs_result else (_expr right_op RIGHT_BOUND) > else: > _expr_result = _lhs_result and (_expr right_op RIGHT_BOUND) > > This allows types like NumPy arrays to control the behaviour of chained > comparisons by returning circuit breakers from comparison operations. > > > Existence checking comparisons > ------------------------------ > > Two new builtins implementing the new protocol are proposed to encapsulate > the > notion of "existence checking": seeing if a value is ``None`` and either > falling back to an alternative value (an operation known as > "None-coalescing") > or passing it through as the result of the overall expression (an operation > known as "None-severing" or "None-propagating"). > > These builtins would be defined as follows:: > > class CircuitBreaker: > """Base circuit breaker type (available as types.CircuitBreaker)""" > def __init__(self, value, condition, inverse_type): > self.value = value > self._condition = condition > self._inverse_type = inverse_type > def __bool__(self): > return self._condition > def __not__(self): > return self._inverse_type(self.value) > def __then__(self): > return self.value > def __else__(self, other): > if other is self: > return self.value > return other > > class exists(types.CircuitBreaker): > """Circuit breaker for 'EXPR is not None' checks""" > def __init__(self, value): > super().__init__(value, value is not None, missing) > > class missing(types.CircuitBreaker): > """Circuit breaker for 'EXPR is None' checks""" > def __init__(self, value): > super().__init__(value, value is None, exists) > > Aside from changing the definition of ``__bool__`` to be based on > ``is not None`` rather than normal truth checking, the key characteristic > of > ``exists`` is that when it is used as a circuit breaker, it is *ephemeral*: > when it is told that short circuiting has taken place, it returns the > original > value, rather than the existence checking wrapper. > > ``missing`` is defined as the logically inverted counterpart of ``exists``: > ``not exists(obj)`` is semantically equivalent to ``missing(obj)``. > > The ``__else__`` implementations for both builtin circuit breakers are > defined > such that the wrapper will always be removed even if you explicitly pass > the > circuit breaker to both sides of the ``else`` expression:: > > breaker = exists(foo) > assert (breaker else breaker) is foo > breaker = missing(foo) > assert (breaker else breaker) is foo > > > Other conditional constructs > ---------------------------- > > No changes are proposed to if statements, while statements, conditional > expressions, comprehensions, or generator expressions, as the boolean > clauses > they contain are already used for control flow purposes. > > However, it's worth noting that while such proposals are outside the scope > of > this PEP, the circuit breaking protocol defined here would be sufficient to > support constructs like:: > > while exists(dynamic_query()) as result: > ... # Code using result > > and: > > if exists(re.search(pattern, text)) as match: > ... # Code using match > > Leaving the door open to such a future extension is the main reason for > recommending that circuit breaker implementations handle the ``self is > other`` > case in ``__else__`` implementations the same way as they handle the > short-circuiting behaviour in ``__then__``. > > > Style guide recommendations > --------------------------- > > The following additions to PEP 8 are proposed in relation to the new > features > introduced by this PEP: > > * In the absence of other considerations, prefer the use of the builtin > circuit breakers ``exists`` and ``missing`` over the corresponding > conditional expressions > > * Do not combine conditional expressions (``if-else``) and circuit breaking > expressions (the ``else`` operator) in a single expression - use one or > the > other depending on the situation, but not both. > > > Rationale > ========= > > Adding a new operator > --------------------- > > Similar to PEP 335, early drafts of this PEP focused on making the existing > ``and`` and ``or`` operators less rigid in their interpretation, rather > than > proposing new operators. However, this proved to be problematic for a few > reasons: > > * defining a shared protocol for both ``and`` and ``or`` was confusing, as > ``__then__`` was the short-circuiting outcome for ``or``, while > ``__else__`` > was the short-circuiting outcome for ``and`` > * the ``and`` and ``or`` operators have a long established and stable > meaning, > so readers would inevitably be surprised if their meaning now became > dependent on the type of the left operand. Even new users would be > confused > by this change due to 25+ years of teaching material that assumes the > current well-known semantics for these operators > * Python interpreter implementations, including CPython, have taken > advantage > of the existing semantics of ``and`` and ``or`` when defining runtime and > compile time optimisations, which would all need to be reviewed and > potentially discarded if the semantics of those operations changed > > Proposing a single new operator instead resolves all of those issues - > ``__then__`` always indicates short circuiting, ``__else__`` only indicates > "short circuiting" if the circuit breaker itself is also passed in as the > right operand, and the semantics of ``and`` and ``or`` remain entirely > unchanged. While the semantics of the unary ``not`` operator do change, the > invariant required of ``__not__`` implementations means that existing > expression optimisations in boolean contexts will remain valid. > > As a result of that design simplification, the new protocol and operator > would > even allow us to expose ``operator.true`` and ``operator.false`` > as circuit breaker definitions if we chose to do so:: > > class true(types.CircuitBreaker): > """Circuit breaker for 'bool(EXPR)' checks""" > def __init__(self, value): > super().__init__(value, bool(value), when_false) > > class false(types.CircuitBreaker): > """Circuit breaker for 'not bool(EXPR)' checks""" > def __init__(self, value): > super().__init__(value, not bool(value), when_true) > > Given those circuit breakers: > > * ``LHS or RHS`` would be roughly ``operator.true(LHS) else RHS`` > * ``LHS and RHS`` would be roughly ``operator.false(LHS) else RHS`` > > > Naming the operator and protocol > -------------------------------- > > The names "circuit breaking operator", "circuit breaking protocol" and > "circuit breaker" are all inspired by the phrase "short circuiting > operator": > the general language design term for operators that only conditionally > evaluate their right operand. > > The electrical analogy is that circuit breakers in Python detect and handle > short circuits in expressions before they trigger any exceptions similar > to the > way that circuit breakers detect and handle short circuits in electrical > systems before they damage any equipment or harm any humans. > > The Python level analogy is that just as a ``break`` statement lets you > terminate a loop before it reaches its natural conclusion, a circuit > breaking > expression lets you terminate evaluation of the expression and produce a > result > immediately. > > > Using an existing keyword > ------------------------- > > Using an existing keyword has the benefit of allowing the new expression to > be introduced without a ``__future__`` statement. > > ``else`` is semantically appropriate for the proposed new protocol, and the > only syntactic ambiguity introduced arises when the new operator is > combined > with the explicit ``if-else`` conditional expression syntax. > > > Element-wise chained comparisons > -------------------------------- > > In ultimately rejecting PEP 335, Guido van Rossum noted [1_]: > > The NumPy folks brought up a somewhat separate issue: for them, > the most common use case is chained comparisons (e.g. A < B < C). > > To understand this observation, we first need to look at how comparisons > work > with NumPy arrays:: > > >>> import numpy as np > >>> increasing = np.arange(5) > >>> increasing > array([0, 1, 2, 3, 4]) > >>> decreasing = np.arange(4, -1, -1) > >>> decreasing > array([4, 3, 2, 1, 0]) > >>> increasing < decreasing > array([ True, True, False, False, False], dtype=bool) > > Here we see that NumPy array comparisons are element-wise by default, > comparing > each element in the lefthand array to the corresponding element in the > righthand > array, and producing a matrix of boolean results. > > If either side of the comparison is a scalar value, then it is broadcast > across > the array and compared to each individual element:: > > >>> 0 < increasing > array([False, True, True, True, True], dtype=bool) > >>> increasing < 4 > array([ True, True, True, True, False], dtype=bool) > > However, this broadcasting idiom breaks down if we attempt to use chained > comparisons:: > > >>> 0 < increasing < 4 > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > ValueError: The truth value of an array with more than one element > is ambiguous. Use a.any() or a.all() > > The problem is that internally, Python implicitly expands this chained > comparison into the form:: > > >>> 0 < increasing and increasing < 4 > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > ValueError: The truth value of an array with more than one element > is ambiguous. Use a.any() or a.all() > > And NumPy only permits implicit coercion to a boolean value for > single-element > arrays where ``a.any()`` and ``a.all()`` can be assured of having the same > result:: > > >>> np.array([False]) and np.array([False]) > array([False], dtype=bool) > >>> np.array([False]) and np.array([True]) > array([False], dtype=bool) > >>> np.array([True]) and np.array([False]) > array([False], dtype=bool) > >>> np.array([True]) and np.array([True]) > array([ True], dtype=bool) > > The proposal in this PEP would allow this situation to be changed by > updating > the definition of element-wise comparison operations in NumPy to return a > dedicated subclass that implements the new circuit breaking protocol and > also > changes the result array's interpretation in a boolean context to always > return ``False`` and hence never trigger the short-circuiting behaviour:: > > class ComparisonResultArray(np.ndarray): > def __bool__(self): > return False > def _raise_NotImplementedError(self): > msg = ("Comparison array truth values are ambiguous outside " > "chained comparisons. Use a.any() or a.all()") > raise NotImplementedError(msg) > def __not__(self): > self._raise_NotImplementedError() > def __then__(self): > self._raise_NotImplementedError() > def __else__(self, other): > return np.logical_and(self, other.view(ComparisonResultArray)) > > With this change, the chained comparison example above would be able to > return:: > > >>> 0 < increasing < 4 > ComparisonResultArray([ False, True, True, True, False], dtype=bool) > > > Existence checking expressions > ------------------------------ > > An increasingly common requirement in modern software development is the > need > to work with "semi-structured data": data where the structure of the data > is > known in advance, but pieces of it may be missing at runtime, and the > software > manipulating that data is expected to degrade gracefully (e.g. by omitting > results that depend on the missing data) rather than failing outright. > > Some particularly common cases where this issue arises are: > > * handling optional application configuration settings and function > parameters > * handling external service failures in distributed systems > * handling data sets that include some partial records > > At the moment, writing such software in Python can be genuinely awkward, as > your code ends up littered with expressions like: > > * ``value1 = expr1.field.of.interest if expr1 is not None else None`` > * ``value2 = expr2["field"]["of"]["interest"] if expr2 is not None else > None`` > * ``value3 = expr3 if expr3 is not None else expr4 if expr4 is not > None else expr5`` > > PEP 531 goes into more detail on some of the challenges of working with > this > kind of data, particularly in data transformation pipelines where dealing > with > potentially missing content is the norm rather than the exception. > > The combined impact of the proposals in this PEP is to allow the above > sample > expressions to instead be written as: > > * ``value1 = missing(expr1) else expr1.field.of.interest`` > * ``value2 = missing(expr2) else expr2.["field"]["of"]["interest"]`` > * ``value3 = exists(expr3) else exists(expr4) else expr5`` > > In these forms, significantly more of the text presented to the reader is > immediately relevant to the question "What does this code do?", while the > boilerplate code to handle missing data by passing it through to the output > or falling back to an alternative input, has shrunk to two uses of the new > ``missing`` builtin, and two uses of the new ``exists`` builtin. > > In the first two examples, the 31 character boilerplate suffix > ``if exprN is not None else None`` (minimally 27 characters for a single > letter > variable name) has been replaced by a 19 character ``missing(expr1) else`` > prefix (minimally 15 characters with a single letter variable name), > markedly > improving the signal-to-pattern-noise ratio of the lines (especially if it > encourages the use of more meaningful variable and field names rather than > making them shorter purely for the sake of expression brevity). The > additional > syntactic sugar proposals in PEP 505 would further reduce this boilerplate > to > a single ``?`` character that also eliminated the repetition of the > expession > being checked for existence. > > In the last example, not only are two instances of the 21 character > boilerplate, > `` if exprN is not None`` (minimally 17 characters) replaced with the > 8 character function call ``exists()``, but that function call is placed > directly around the original expression, eliminating the need to duplicate > it > in the conditional existence check. > > > Risks and concerns > ================== > > This PEP has been designed specifically to address the risks and concerns > raised when discussing PEPs 335, 505 and 531. > > * it defines a new operator and adjusts the definition of chained > comparison > rather than impacting the existing ``and`` and ``or`` operators > * the changes to the ``not`` unary operator are defined in such a way that > control flow optimisations based on the existing semantics remain valid > * rather than the cryptic ``??``, it uses ``else`` as the operator keyword > in > exactly the same sense as it is already used in conditional expressions > * it defines a general purpose short-circuiting binary operator that can > even > be used to express the existing semantics of ``and`` and ``or`` rather > than > focusing solely and inflexibly on existence checking > * it names the proposed builtins in such a way that they provide a strong > mnemonic hint as to when the expression containing them will > short-circuit > and skip evaluating the right operand > > > Possible confusion with conditional expressions > ----------------------------------------------- > > The proposal in this PEP is essentially for an "implied ``if``" where if > you > omit the ``if`` clause from a conditional expression, you invoke the > circuit > breaking protocol instead. That is:: > > exists(foo) else calculate_default() > > invokes the new protocol, but:: > > foo.field.of.interest if exists(foo) else calculate_default() > > bypasses it entirely, *including* the non-short-circuiting ``__else__`` > method. > > This mostly wouldn't be a problem for the proposed ``types.CircuitBreaker`` > implementation (and hence the ``exists`` and ``missing`` builtins), as the > only purpose the extended protocol serves in that case is to remove the > wrapper in the short-circuiting case - the ``__else__`` method passes the > right operand through unchanged. > > However, this discrepancy could potentially be eliminated entirely by also > updating conditional expressions to use the circuit breaking protocol if > the condition defines those methods. In that case, ``__then__`` would need > to be updated to accept the left operand as a parameter, with > short-circuiting > indicated by passing in the circuit breaker itself:: > > class CircuitBreaker: > """Base circuit breaker type (available as types.CircuitBreaker)""" > def __init__(self, value, condition, inverse_type): > self.value = value > self._condition = condition > self._inverse_type = inverse_type > def __bool__(self): > return self._condition > def __not__(self): > return self._inverse_type(self.value) > def __then__(self, other): > if other is not self: > return other > return self.value # Short-circuit, remove the wrapper > def __else__(self, other): > if other is not self: > return other > return self.value # Short-circuit, remove the wrapper > > With this symmetric protocol, the definition of conditional expressions > could be updated to also make the ``else`` clause optional:: > > test: else_test ['if' or_test ['else' test]] | lambdef > else_test: or_test ['else' test] > > (We would avoid the apparent simplification to ``else_test ('if' > else_test)*`` > in order to make it easier to correctly preserve the semantics of normal > conditional expressions) > > Given that expanded definition, the following statements would be > functionally equivalent:: > > foo = calculate_default() if missing(foo) > foo = calculate_default() if foo is None else foo > > Just as the base proposal already makes the following equivalent:: > > foo = exists(foo) else calculate_default() > foo = foo if foo is not None else calculate_default() > > The ``if`` based circuit breaker form has the virtue of reading > significantly > better when used for conditional imperative commands like debug messages:: > > print(some_expensive_query()) if verbosity > 2 > > If we went down this path, then ``operator.true`` would need to be declared > as the nominal implicit circuit breaker when the condition didn't define > the > circuit breaker protocol itself (so the above example would produce > ``None`` > if the debugging message was printed, and ``False`` otherwise) > > The main objection to this expansion of the proposal is that it makes it a > more intrusive change that may potentially affect the behaviour of existing > code, while the main point in its favour is that allowing both ``if`` and > ``else`` as circuit breaking operators and also supporting the circuit > breaking > protocol for normal conditional expressions would be significantly more > self-consistent than special-casing a bare ``else`` as a stand-alone > operator. > > > Design Discussion > ================= > > Arbitrary sentinel objects > -------------------------- > > Unlike PEPs 505 and 531, this proposal readily handles custom sentinel > objects:: > > class defined(types.CircuitBreaker): > MISSING = object() > def __init__(self, value): > super().__init__(self, value is not self.MISSING, undefined) > > class undefined(types.CircuitBreaker): > def __init__(self, value): > super().__init__(self, value is defined.MISSING, defined) > > # Using the sentinel to check whether or not an argument was supplied > def my_func(arg=defined.MISSING): > arg = defined(arg) else calculate_default() > > > Implementation > ============== > > As with PEP 505, actual implementation has been deferred pending > in-principle > interest in the idea of making these changes - aside from the possible > syntactic ambiguity concerns covered by the grammer proposals above, the > implementation isn't really the hard part of these proposals, the hard part > is deciding whether or not this is a change where the long term benefits > for > new and existing Python users outweigh the short term costs involved in the > wider ecosystem (including developers of other implementations, language > curriculum developers, and authors of other Python related educational > material) adjusting to the change. > > ...TBD... > > > Acknowledgements > ================ > > Thanks go to Mark E. Haase for feedback on and contributions to earlier > drafts > of this proposal. However, his clear and exhaustive explanation of the > original > protocol design that modified the semantics of ``if-else`` conditional > expressions to use an underlying ``__then__``/``__else__`` protocol helped > convince me it was too complicated to keep, so this iteration contains > neither > that version of the protocol, nor Mark's explanation of it. > > > References > ========== > > .. [1] PEP 335 rejection notification > (http://mail.python.org/pipermail/python-dev/2012-March/117510.html) > > Copyright > ========= > > This document has been placed in the public domain under the terms of the > CC0 1.0 license: https://creativecommons.org/publicdomain/zero/1.0/ > > > -- > Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/