Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 308 – Conditional Expressions

Author:
Guido van Rossum, Raymond Hettinger
Status:
Final
Type:
Standards Track
Created:
07-Feb-2003
Python-Version:
2.5
Post-History:
07-Feb-2003, 11-Feb-2003

Table of Contents

Adding a conditional expression

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

References

[1]
Pronouncementhttps://mail.python.org/pipermail/python-dev/2005-September/056846.html
[2]
Motivating use case:https://mail.python.org/pipermail/python-dev/2005-September/056546.htmlhttps://mail.python.org/pipermail/python-dev/2005-September/056510.html
[3]
Review in the context of real-world code fragments:https://mail.python.org/pipermail/python-dev/2005-September/056803.html

Introduction to earlier draft of the PEP (kept for historical purposes)

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.

Proposal

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:

  • First, <condition> is evaluated.
  • If <condition> is true, <expression1> is evaluated and is theresult of the whole thing.
  • If <condition> is false, <expression2> is evaluated and is theresult of the whole thing.

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:

  • the required parentheses
  • confusability with statement syntax
  • additional semantic loading of colons

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.

Alternatives

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.

Summary of the Current State of the Discussion

Groups are falling into one of three camps:

  1. Adopt a ternary operator built using punctuation characters:
    <condition> ? <expression1> : <expression2>
  2. Adopt a ternary operator built using new or existing keywords.The leading examples are:
    <condition>then<expression1>else<expression2>(if<condition>:<expression1>else:<expression2>)
  3. Do nothing.

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.

Short-Circuit Behavior

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:

  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 theexpression.
#  Example where all three reasons applydata = 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 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.

Detailed Results of Voting

Votesrejectingalloptions:82Voteswithrankordering:436---Totalvotesreceived:518ACCEPTREJECTTOTAL-----------------------------------------------Rank1Rank2Rank3Rank1Rank2Rank3LetterA513319182020161B45462192423168C945429202018235D71403152831206E77103532F14191071767G761012430H2022174102598I16209552075J617511039K1641324L12339M734251132N234211O16514926P53615727Q18715651162Z11----------------------Total363286202731492301303RejectAll828282246----------------------Total3632862021552313121549

CHOICE KEY

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

Detail for write-in votes and their ranking

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)

Copyright

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


[8]ページ先頭

©2009-2025 Movatter.jp