On 9/29/2005, Guido decided to add conditional expressions in theform of “X if C else Y”.[1]
The motivating use case was the prevalence of error-prone attemptsto achieve the same effect using “and” and “or”.[2]
Previous community efforts to add a conditional expression werestymied by a lack of consensus on the best syntax. That issue wasresolved by simply deferring to a BDFL best judgment call.
The decision was validated by reviewing how the syntax fared whenapplied throughout the standard library (this review approximates asampling of real-world use cases, across a variety of applications,written by a number of programmers with diverse backgrounds).[3]
The following change will be made to the grammar. (The or_testsymbols is new, the others are modified.)
test:or_test['if'or_test'else'test]|lambdefor_test:and_test('or'and_test)*...testlist_safe:or_test[(','or_test)+[',']]...gen_for:'for'exprlist'in'or_test[gen_iter]
The new syntax nearly introduced a minor syntactical backwardsincompatibility. In previous Python versions, the following islegal:
[fforfinlambdax:x,lambdax:x**2iff(1)==1]
(I.e. a list comprehension where the sequence following ‘in’ is anunparenthesized series of lambdas – or just one lambda, even.)
In Python 3.0, the series of lambdas will have to beparenthesized, e.g.:
[fforfin(lambdax:x,lambdax:x**2)iff(1)==1]
This is because lambda binds less tight than the if-elseexpression, but in this context, the lambda could already befollowed by an ‘if’ keyword that binds less tightly still (fordetails, consider the grammar changes shown above).
However, in Python 2.5, a slightly different grammar is used thatis more backwards compatible, but constrains the grammar of alambda used in this position by forbidding the lambda’s body tocontain an unparenthesized condition expression. Examples:
[fforfin(1,lambdax:xifx>=0else-1)]# OK[fforfin1,(lambdax:xifx>=0else-1)]# OK[fforfin1,lambdax:(xifx>=0else-1)]# OK[fforfin1,lambdax:xifx>=0else-1]# INVALID
Requests for an if-then-else (“ternary”) expression keep coming upon comp.lang.python. This PEP contains a concrete proposal of afairly Pythonic syntax. This is the community’s one chance: ifthis PEP is approved with a clear majority, it will be implementedin Python 2.4. If not, the PEP will be augmented with a summaryof the reasons for rejection and the subject better not come upagain. While the BDFL is co-author of this PEP, he is neither infavor nor against this proposal; it is up to the community todecide. If the community can’t decide, the BDFL will reject thePEP.
After unprecedented community response (very good arguments weremade both pro and con) this PEP has been revised with the help ofRaymond Hettinger. Without going through a complete revisionhistory, the main changes are a different proposed syntax, anoverview of proposed alternatives, the state of the currentdiscussion, and a discussion of short-circuit behavior.
Following the discussion, a vote was held. While there was an overallinterest in having some form of if-then-else expressions, no oneformat was able to draw majority support. Accordingly, the PEP wasrejected due to the lack of an overwhelming majority for change.Also, a Python design principle has been to prefer the status quowhenever there are doubts about which path to take.
The proposed syntax is as follows:
(if<condition>:<expression1>else:<expression2>)
Note that the enclosing parentheses are not optional.
The resulting expression is evaluated like this:
A natural extension of this syntax is to allow one or more ‘elif’parts:
(if<cond1>:<expr1>elif<cond2>:<expr2>...else:<exprN>)
This will be implemented if the proposal is accepted.
The downsides to the proposal are:
Note that at most one of <expression1> and <expression2> isevaluated. This is called a “short-circuit expression”; it issimilar to the way the second operand of ‘and’ / ‘or’ is onlyevaluated if the first operand is true / false.
A common way to emulate an if-then-else expression is:
<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 thatwork – however, they are pretty ugly and require much more effortto understand.
Holger Krekel proposed a new, minimally invasive variant:
<condition>and<expression1>else<expression2>
The concept behind it is that a nearly complete ternary operatoralready exists with and/or and this proposal is the least invasivechange that makes it complete. Many respondants on thenewsgroup found this to be the most pleasing alternative.However, a couple of respondants were able to post examplesthat were mentally difficult to parse. Later it was pointedout that this construct works by having the “else” change theexisting meaning of “and”.
As a result, there is increasing support for Christian Tismer’sproposed variant of the same idea:
<condition>then<expression1>else<expression2>
The advantages are simple visual parsing, no required parentheses,no change in the semantics of existing keywords, not as likelyas the proposal to be confused with statement syntax, and doesnot further overload the colon. The disadvantage is theimplementation costs of introducing a new keyword. However,unlike other new keywords, the word “then” seems unlikely tohave been used as a name in existing programs.
—
Many C-derived languages use this syntax:
<condition> ? <expression1> : <expression2>
Eric Raymond even implemented this. The BDFL rejected this forseveral reasons: the colon already has many uses in Python (eventhough it would actually not be ambiguous, because the questionmark requires a matching colon); for people not used to C-derivedlanguage, it is hard to understand.
—
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 uncomfortablefor many of participants in the discussion; especially when<expression1> is long, it’s easy to miss the conditional whileskimming.
—
Some have suggested adding a new builtin instead of extending thesyntax of the language. For example:
cond(<condition>,<expression1>,<expression2>)
This won’t work the way a syntax extension will because bothexpression1 and expression2 must be evaluated before the functionis called. There’s no way to short-circuit the expressionevaluation. It could work if ‘cond’ (or some other name) weremade a keyword, but that has all the disadvantages of adding a newkeyword, plus confusing syntax: itlooks like a function call soa casual reader might expect both <expression1> and <expression2>to be evaluated.
Groups are falling into one of three camps:
<condition> ? <expression1> : <expression2>
<condition>then<expression1>else<expression2>(if<condition>:<expression1>else:<expression2>)
The first two positions are relatively similar.
Some find that any form of punctuation makes the language morecryptic. Others find that punctuation style is appropriate forexpressions rather than statements and helps avoid a COBOL style:3 plus 4 times 5.
Adapting existing keywords attempts to improve on punctuationthrough explicit meaning and a more tidy appearance. The downsideis some loss of the economy-of-expression provided by punctuationoperators. The other downside is that it creates some degree ofconfusion between the two meanings and two usages of the keywords.
Those difficulties are overcome by options which introduce newkeywords which take more effort to implement.
The last position is doing nothing. Arguments in favor includekeeping the language simple and concise; maintaining backwardscompatibility; and that any every use case can already be alreadyexpressed in terms of “if” and “else”. Lambda expressions are anexception as they require the conditional to be factored out intoa separate function definition.
The arguments against doing nothing are that the other choicesallow greater economy of expression and that current practicesshow a propensity for erroneous uses of “and”, “or”, or one theirmore complex, less visually unappealing workarounds.
The principal difference between the ternary operator and thecond()
function is that the latter provides an expression form butdoes not provide short-circuit evaluation.
Short-circuit evaluation is desirable on three occasions:
# Example where all three reasons applydata = isinstance(source, file) ? source.readlines() : source.split()
readlines()
moves the file pointersplit()
is only valid for strings andreadlines()
is onlyvalid for file objects.Supporters of acond()
function point out that the need forshort-circuit evaluation is rare. Scanning through existing codedirectories, they found that if/else did not occur often; and ofthose only a few contained expressions that could be helped bycond()
or a ternary operator; and that most of those had no needfor short-circuit evaluation. Hence,cond()
would suffice formost needs and would spare efforts to alter the syntax of thelanguage.
More supporting evidence comes from scans of C code bases whichshow that its ternary operator used very rarely (as a percentageof lines of code).
A counterpoint to that analysis is that the availability of aternary operator helped the programmer in every case because itspared the need to search for side-effects. Further, it wouldpreclude errors arising from distant modifications which introduceside-effects. The latter case has become more of a reality withthe advent of properties where even attribute access can be givenside-effects.
The BDFL’s position is that short-circuit behavior is essentialfor an if-then-else construct to be added to the language.
Votesrejectingalloptions:82Voteswithrankordering:436---Totalvotesreceived:518ACCEPTREJECTTOTAL-----------------------------------------------Rank1Rank2Rank3Rank1Rank2Rank3LetterA513319182020161B45462192423168C945429202018235D71403152831206E77103532F14191071767G761012430H2022174102598I16209552075J617511039K1641324L12339M734251132N234211O16514926P53615727Q18715651162Z11----------------------Total363286202731492301303RejectAll828282246----------------------Total3632862021552313121549
A. x if C else yB. if C then x else yC. (if C: x else: y)D. C ? x : yE. C ? x ! yF. cond(C, x, y)G. C ?? x || yH. C then x else yI. x when C else yJ. C ? x else yK. C -> x else yL. C -> (x, y)M. [x if C else y]N. ifelse C: x else yO. <if C then x else y>P. C and x else yQ. any write-in vote
3: Q reject y x C elsethenif2: Q accept (C ? x ! y)3: Q reject ...3: Q accept ? C : x : y3: Q accept (x if C, y otherwise)3: Q reject ...3: Q reject NONE1: Q accept select : (<c1> : <val1>; [<cx> : <valx>; ]* elseval)2: Q reject if C: t else: f3: Q accept C selects x else y2: Q accept iff(C, x, y) # "if-function"1: Q accept (y, x)[C]1: Q accept C true: x false: y3: Q accept C then: x else: y3: Q reject3: Q accept (if C: x elif C2: y else: z)3: Q accept C -> x : y1: Q accept x (if C), y1: Q accept if c: x else: y3: Q accept (c).{True:1, False:2}2: Q accept if c: x else: y3: Q accept (c).{True:1, False:2}3: Q accept if C: x else y1: Q accept (x if C else y)1: Q accept ifelse(C, x, y)2: Q reject x or y <- C1: Q accept (C ? x : y) required parens1: Q accept iif(C, x, y)1: Q accept ?(C, x, y)1: Q accept switch-case2: Q accept multi-line if/else1: Q accept C: x else: y2: Q accept (C): x else: y3: Q accept if C: x else: y1: Q accept x if C, else y1: Q reject choice: c1->a; c2->b; ...; z3: Q accept [if C then x else y]3: Q reject no other choice has x as the first element1: Q accept (x,y) ? C3: Q accept x if C else y (The "else y" being optional)1: Q accept (C ? x , y)1: Q accept any outcome (i.e form or plain rejection) from a usability study1: Q reject (x if C else y)1: Q accept (x if C else y)2: Q reject NONE3: Q reject NONE3: Q accept (C ? x else y)3: Q accept x when C else y2: Q accept (x if C else y)2: Q accept cond(C1, x1, C2, x2, C3, x3,...)1: Q accept (if C1: x elif C2: y else: z)1: Q reject cond(C, :x, :y)3: Q accept (C and [x] or [y])[0]2: Q reject3: Q reject3: Q reject all else1: Q reject no-change3: Q reject deliberately omitted as I have no interest in any other proposal2: Q reject (C then x else Y)1: Q accept if C: x else: y1: Q reject (if C then x else y)3: Q reject C?(x, y)
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0308.rst
Last modified:2025-02-01 08:59:27 GMT