2003-02-07 12:03:31 -05:00
|
|
|
|
PEP: 308
|
|
|
|
|
Title: If-then-else expression
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
2003-02-11 00:43:56 -05:00
|
|
|
|
Author: Guido van Rossum and Raymond D. Hettinger
|
2003-02-09 00:12:54 -05:00
|
|
|
|
Status: Draft
|
2003-02-07 12:03:31 -05:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/plain
|
|
|
|
|
Created: 7-Feb-2003
|
2003-02-11 09:59:18 -05:00
|
|
|
|
Post-History: 7-Feb-2003, 11-Feb-2003
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Introduction
|
|
|
|
|
|
|
|
|
|
Requests for an if-then-else ("ternary") expression keep coming up
|
|
|
|
|
on comp.lang.python. This PEP contains a concrete proposal of a
|
|
|
|
|
fairly Pythonic syntax. This is the community's one chance: if
|
|
|
|
|
this PEP is approved with a clear majority, it will be implemented
|
|
|
|
|
in Python 2.4. If not, the PEP will be augmented with a summary
|
|
|
|
|
of the reasons for rejection and the subject better not come up
|
|
|
|
|
again. While I am the author of this PEP, I am neither in favor
|
|
|
|
|
nor against this proposal; it is up to the community to decide.
|
|
|
|
|
If the community can't decide, I'll reject the PEP.
|
|
|
|
|
|
2003-02-11 09:59:18 -05:00
|
|
|
|
After unprecedented community response (very food arguments were
|
|
|
|
|
made both pro and con) this PEP has been revised with the help of
|
|
|
|
|
Raymond Hettinger. Without going through a complete revision
|
|
|
|
|
history, the main changes are a different proposed syntax, an
|
|
|
|
|
overview of proposed alternatives, the state of the curent
|
|
|
|
|
discussion, and a discussion of short-circuit behavior.
|
|
|
|
|
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
|
|
|
|
Proposal
|
|
|
|
|
|
|
|
|
|
The proposed syntax is as follows:
|
|
|
|
|
|
2003-02-11 00:43:56 -05:00
|
|
|
|
(if <condition>: <expression1> else: <expression2>)
|
|
|
|
|
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
|
|
|
|
This is evaluated like this:
|
|
|
|
|
|
|
|
|
|
- First, <condition> is evaluated.
|
|
|
|
|
|
|
|
|
|
- If <condition> is true, <expression1> is evaluated and is the
|
|
|
|
|
result of the whole thing.
|
|
|
|
|
|
|
|
|
|
- If <condition> is false, <expression2> is evaluated and is the
|
|
|
|
|
result of the whole thing.
|
|
|
|
|
|
|
|
|
|
Note that at most one of <expression1> and <expression2> is
|
2003-02-11 09:59:18 -05:00
|
|
|
|
evaluated. This is called a "short-circuit expression"; it is
|
|
|
|
|
similar to the way the second operand of 'and' / 'or' is only
|
|
|
|
|
evaluated if the first operand is true / false.
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
2003-02-11 09:59:18 -05:00
|
|
|
|
A common way to emulate an if-then-else expression is:
|
2003-02-07 19:56:13 -05:00
|
|
|
|
|
|
|
|
|
<condition> and <expression1> or <expression2>
|
|
|
|
|
|
|
|
|
|
However, this doesn't work the same way: it returns <expression2>
|
|
|
|
|
when <expression1> is false! See FAQ 4.16 for alternatives that
|
2003-02-11 09:59:18 -05:00
|
|
|
|
work -- however, they are pretty ugly and require much more effort
|
|
|
|
|
to understand.
|
2003-02-07 19:56:13 -05:00
|
|
|
|
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
|
|
|
|
Alternatives
|
|
|
|
|
|
2003-02-11 00:43:56 -05:00
|
|
|
|
The original version of this PEP proposed the following syntax:
|
|
|
|
|
|
|
|
|
|
<expression1> if <condition> else <expression2>
|
|
|
|
|
|
|
|
|
|
The out-of-order arrangement was found to be too uncomfortable
|
2003-02-11 09:59:18 -05:00
|
|
|
|
for many of participants in the discussion; especially when
|
|
|
|
|
<expression1> is long, it's easy to miss the conditional while
|
|
|
|
|
skimming.
|
2003-02-11 00:43:56 -05:00
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2003-02-07 12:03:31 -05:00
|
|
|
|
Many C-derived languages use this syntax:
|
|
|
|
|
|
|
|
|
|
<condition> ? <expression1> : <expression2>
|
|
|
|
|
|
2003-02-11 00:43:56 -05:00
|
|
|
|
Eric Raymond even implemented this. The BDFL rejected this for
|
|
|
|
|
several reasons: the colon already has many uses in Python (even
|
2003-02-11 09:59:18 -05:00
|
|
|
|
though it would actually not be ambiguous, because the question
|
|
|
|
|
mark requires a matching colon); for people not used to C-derived
|
2003-02-07 19:56:13 -05:00
|
|
|
|
language, it is hard to understand.
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
2003-02-07 17:34:54 -05:00
|
|
|
|
---
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
2003-02-07 19:56:13 -05:00
|
|
|
|
David Ascher proposed a variant that doesn't have this problem:
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
|
|
|
|
<condition> ? <expression1> ! <expression2>
|
|
|
|
|
|
|
|
|
|
While cute, this suffers from the Perlish problem of using
|
|
|
|
|
arbitrary punctuation with an arbitrary meaning; and it's no
|
|
|
|
|
easier to understand than the ?: form.
|
|
|
|
|
|
2003-02-07 17:34:54 -05:00
|
|
|
|
---
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
2003-02-11 00:43:56 -05:00
|
|
|
|
Raymond Hettinger proposed a variant that removes the
|
|
|
|
|
arbitrariness:
|
2003-02-07 17:34:54 -05:00
|
|
|
|
|
2003-02-11 09:59:18 -05:00
|
|
|
|
<condition> ?? <expression1> || <expression2>
|
2003-02-07 17:34:54 -05:00
|
|
|
|
|
2003-02-11 00:43:56 -05:00
|
|
|
|
The ?? and || are not arbitrary as they strongly suggest testing
|
|
|
|
|
and alternation. Another merit is that that existing operators
|
2003-02-11 09:59:18 -05:00
|
|
|
|
are not overloaded. Having two characters at each step also helps
|
|
|
|
|
visually separate the subordinate expressions. Alas, the BDFL
|
|
|
|
|
prefers the proposed syntax and considers this alternative "too
|
|
|
|
|
Perlish".
|
2003-02-07 17:34:54 -05:00
|
|
|
|
|
|
|
|
|
---
|
2003-02-07 17:13:53 -05:00
|
|
|
|
|
2003-02-07 15:18:45 -05:00
|
|
|
|
Many people suggest adding a new builtin instead of extending the
|
|
|
|
|
syntax of the language, e.g.:
|
|
|
|
|
|
2003-02-11 09:59:18 -05:00
|
|
|
|
cond(<condition>, <expression1>, <expression2>)
|
2003-02-07 15:18:45 -05:00
|
|
|
|
|
|
|
|
|
This won't work the way a syntax extension will because both
|
|
|
|
|
expression1 and expression2 must be evaluated before the function
|
|
|
|
|
is called. There's no way to short-circuit the expression
|
2003-02-11 09:59:18 -05:00
|
|
|
|
evaluation. It could work if 'cond' (or some other name) were
|
|
|
|
|
made a keyword, but that has all the disadvantages of adding a new
|
|
|
|
|
keyword, plus confusing syntax: it *looks* like a function call so
|
|
|
|
|
a casual reader might expect both <expression1> and <expression2>
|
|
|
|
|
to be evaluated.
|
2003-02-07 12:03:31 -05:00
|
|
|
|
|
2003-02-07 17:29:39 -05:00
|
|
|
|
|
2003-02-11 00:43:56 -05:00
|
|
|
|
Summary of the Current State of the Discussion
|
|
|
|
|
|
|
|
|
|
Groups are falling into one of five camps:
|
|
|
|
|
|
|
|
|
|
1. Adopt a ternary operator built using punctuation characters.
|
|
|
|
|
It would look something like:
|
|
|
|
|
<condition> ?? <expression1> || <expression2>
|
|
|
|
|
|
|
|
|
|
2. Adopt a ternary operator built using existing keywords.
|
|
|
|
|
The proposal listed above is the leading example.
|
|
|
|
|
|
|
|
|
|
3. Adopt a ternary operator built using a new keyword.
|
|
|
|
|
The leading contender looks like this:
|
|
|
|
|
cond(<condition>, <expression1>, <expression2>)
|
|
|
|
|
|
|
|
|
|
4. Adopt a function without short-circuit behavior:
|
|
|
|
|
cond(<condition>, <expression1>, <expression2>)
|
|
|
|
|
|
|
|
|
|
5. Do nothing.
|
|
|
|
|
|
|
|
|
|
The first two positions are relatively similar.
|
|
|
|
|
|
|
|
|
|
Some find that any form of punctuation makes the language more
|
2003-02-11 09:59:18 -05:00
|
|
|
|
cryptic. Others find that punctuation style is appropriate for
|
|
|
|
|
expressions rather than statements and helps avoid a COBOL style:
|
|
|
|
|
3 plus 4 times 5.
|
2003-02-11 00:43:56 -05:00
|
|
|
|
|
|
|
|
|
Adapting existing keywords attempts to improve on punctuation
|
|
|
|
|
through explicit meaning and a more tidy appearance. The downside
|
|
|
|
|
is some loss of the economy-of-expression provided by punctuation
|
|
|
|
|
operators. The other downside is that it creates some degree of
|
|
|
|
|
confusion between the two meanings and two usages of the keywords.
|
|
|
|
|
|
|
|
|
|
The third form introduces a new keyword and arranges the arguments
|
2003-02-11 09:59:18 -05:00
|
|
|
|
separated by commas. Adding a new keyword is to be generally
|
|
|
|
|
avoided. But the form is clear, short, and direct. There is a
|
|
|
|
|
possible confusion with function syntax which implies that all the
|
|
|
|
|
arguments are evaluated rather than short-circuited. This idea
|
|
|
|
|
was presented by the BDFL and should be considered a contender for
|
|
|
|
|
the final vote. The exact keyword is still an open question. One
|
|
|
|
|
proposal was iif(), but it looks like a typo and can be confused
|
|
|
|
|
with if-and-only-if which has a different, well-defined
|
|
|
|
|
mathematical meaning.
|
2003-02-11 00:43:56 -05:00
|
|
|
|
|
|
|
|
|
The fourth position is much more conservative. Adding a new
|
|
|
|
|
function, cond(), is trivially easy to implement and fits easily
|
|
|
|
|
within the existing python model. Users of older versions of
|
2003-02-11 09:59:18 -05:00
|
|
|
|
Python will find it trivial to simulate. The downside is that it
|
|
|
|
|
does not provide the sought-after short-circuit evaluation (see
|
|
|
|
|
the discussion below on the need for this). The bigger downside
|
|
|
|
|
is that the BDFL opposes *any* solution that does not provide
|
|
|
|
|
short circuit behavior.
|
2003-02-11 00:43:56 -05:00
|
|
|
|
|
|
|
|
|
The last position is doing nothing. Arguments in favor include
|
|
|
|
|
keeping the language simple and concise; maintaining backwards
|
|
|
|
|
compatibility; and that any every use cases can already be already
|
2003-02-11 09:59:18 -05:00
|
|
|
|
expressed in terms of "if" and "else". Lambda expressions are an
|
|
|
|
|
exception as they require the conditional to be factored out into
|
|
|
|
|
a separate function definition.
|
2003-02-11 00:43:56 -05:00
|
|
|
|
|
|
|
|
|
The arguments against doing nothing are that the other choices
|
|
|
|
|
allow greater economy of expression and that current practices
|
|
|
|
|
show a propensity for erroneous uses of "and", "or", or one their
|
|
|
|
|
more complex, visually unappealing workarounds.
|
|
|
|
|
|
|
|
|
|
It should also be mentioned that most supporters of any of the
|
2003-02-11 09:59:18 -05:00
|
|
|
|
first four positions do not want an imperfect solution and would
|
|
|
|
|
sooner have no change than create a wart to attain their desired
|
|
|
|
|
functionality.
|
2003-02-11 00:43:56 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Short-Circuit Behavior
|
|
|
|
|
|
2003-02-11 09:59:18 -05:00
|
|
|
|
The principal difference between the ternary operator and the
|
|
|
|
|
cond() function is that the latter provides an expression form but
|
|
|
|
|
does not provide short-circuit evaluation.
|
2003-02-11 00:43:56 -05:00
|
|
|
|
|
|
|
|
|
Short-circuit evaluation is desirable on three occasions:
|
|
|
|
|
|
|
|
|
|
1. When an expression has side-effects
|
|
|
|
|
2. When one or both of the expressions are resource intensive
|
|
|
|
|
3. When the condition serves as a guard for the validity of the
|
|
|
|
|
expression.
|
|
|
|
|
|
|
|
|
|
# Example where all three reasons apply
|
|
|
|
|
data = isinstance(source, file) ?? source.readlines()
|
|
|
|
|
|| source.split()
|
|
|
|
|
|
|
|
|
|
1. readlines() moves the file pointer
|
|
|
|
|
2. for long sources, both alternatives take time
|
|
|
|
|
3. split() is only valid for strings and readlines() is only
|
|
|
|
|
valid for file objects.
|
|
|
|
|
|
|
|
|
|
Supporters of the cond() function point-out that the need for
|
2003-02-11 09:59:18 -05:00
|
|
|
|
short-circuit evaluation is rare. Scanning through existing code
|
|
|
|
|
directories, they found that if/else did not occur often; and of
|
|
|
|
|
those only a few contained expressions that could be helped by
|
|
|
|
|
cond() or a ternary operator; and that most of those had no need
|
|
|
|
|
for short-circuit evaluation. Hence, cond() would suffice for
|
|
|
|
|
most needs and would spare efforts to alter the syntax of the
|
|
|
|
|
language.
|
|
|
|
|
|
|
|
|
|
More supporting evidence comes from scans of C code bases which
|
|
|
|
|
show that its ternary operator used very rarely (as a percentage
|
|
|
|
|
of lines of code).
|
|
|
|
|
|
|
|
|
|
A counter point to that analysis is that the availability of a
|
|
|
|
|
ternary operator helped the programmer in every case because it
|
|
|
|
|
spared the need to search for side-effects. Further, it would
|
|
|
|
|
preclude errors arising from distant modifications which introduce
|
|
|
|
|
side-effects. The latter case has become more of a reality with
|
|
|
|
|
the advent of properties where even attribute access can be given
|
|
|
|
|
side-effects.
|
|
|
|
|
|
|
|
|
|
The BDFL's position is that short-circuit behavior is essential
|
|
|
|
|
for an if-then-else construct to be added to the language.
|
2003-02-07 21:12:43 -05:00
|
|
|
|
|
2003-02-07 17:29:39 -05:00
|
|
|
|
|
2003-02-07 12:03:31 -05:00
|
|
|
|
Copyright
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
End:
|