在编程时,咱们常常要作条件判别,并根据条件的成果挑选执行不同的句子块。在许多编程言语中,最常见的写法是三元运算符,可是,Python 并不支撑三元运算符,无独有偶,两个最抢手的新兴言语 Go 和 Rust 也不支撑!
为什么 Python 不支撑三元运算符呢?本文将首要剖析 Python 在规划条件挑选语法时的过程,科普为什么它会选用现今的异乎寻常的完结计划,一起,咱们也将调查为什么其它言语也要抛弃传统的三元运算符。
在开篇之前,我再声明一下:就像“Python为什么”系列的大部分文章一样,本文关注的仅是一个很小的语法点,但它并不是“茴香豆有几种写法”那种毫无意义的论题。因为,细微之处见真功夫,深入研究言语规划背面的原因、前史和哲学,能够让咱们在编程时有愈加明晰和自在的思想。
什么是三元运算符?
三元运算符一般指的是“?:”,其语法方法为:condition ? expression1 : expression2
,假如 condition 为真,则取 expression1,若不为真,则取 expression2。
语法简化方法“a ? b : c”,能够读成“假如 a 条件建立,则为 b,否则为 c”。
三元运算符是对普通一重 if-else 结构的简化,常用于在一条句子中一起完结条件判别和取值操作。
// 惯例 if-else
if (a > b) {
result = x;
} else {
result = y;
}
// 简化后的写法
result = a > b ? x : y;
选用了这种语法规划的编程言语有许多,比如 C、C#、C++、Java、JavaScript、PHP、Perl、Ruby、Swift 等等。毫无争议,它便是编程言语界的干流规划计划(至今仍是)。
这种语法十分简练高效,代码的可读性也很强(假如你不是第一次触摸的话),深得许多人的喜欢。
可是,它并非毫无缺陷。Python 是这种语法规划的最著名的挑战者,接下来,咱们将看看为什么 Python 要另辟蹊径。
Python 社区的投票
Python 发布于 1991 年,但在接下来的 15 年里,除了 if-else 语法外,它并不支撑三元运算符和其它条件表达式。并且,在 2006 年引进条件表达式前,社区对此进行了绵长而曲折的争论,能够说这是一个规划得很艰难的语法了。
开始,因为时常有人恳求增加 if-then-else(三元)表达式,因而在 2003 年 2 月,PEP 308 – Conditional Expressions 被提了出来,目的是让社区选出一个让大都人支撑的计划。
很快,除了少部分人期望啥也不做外,社区里呈现了好几种计划:
(1)运用标点符号构建的三元运算符
即惯例的三元运算符,跟前文介绍的语法一样:
<condition> ? <expression1> : <expression2>
这个计划的呼声挺高,有开发者乃至已提交了完结代码。可是,Guido 给出了两个对立的理由:冒号在 Python 中已经有许多用处(即使它实际上不会产生歧义,因为问号需求匹配冒号);关于不习气 C 衍生言语的人来说,了解起来很困难。
(2)运用现有和新的关键字构建
引进新的“then”关键字,结合现有的“else”关键字:
<condition> then <expression1> else <expression2>
它的长处是简单明了、不需求括号、不改变现有关键字的语义,不大或许与句子混杂,并且不需求重载冒号。缺陷是引进新关键字的完结本钱较高。
(3)其它思路
跟上一种计划的思路相似,但没有上述两类计划的支撑度高。
(if <condition>: <expression1> else: <expression2>)
<condition> and <expression1> else <expression2>
<expression1> if <condition> else <expression2>
cond(<condition>, <expression1>, <expression2>)
值得一提的是(if <condition>: <expression1> else: <expression2>)
,它是惯例 if-else 语法的扁平化,简单了解,但缺陷是需求运用圆括号,简单跟生成器表达式混杂,并且需求解释器对冒号做特别化处理。
别的值得一提的是<expression1> if <condition> else <expression2>
,它是 PEP-308 最早版本的推荐计划,可是这种不将条件放在首位的风格让一些人感觉不舒服,并且,当“expression1”很长的时候,很简单就忽略掉它的条件。
其时参与投票的悉数规划计划:
整体上,开发者们期望引进某种方法的 if-then-else 表达式,但投票后却没有哪种计划能取得肯定的优势。概括起来,分歧的问题首要有:是否用标点符号、是否复用关键字、是否复用圆括号、是否引进新关键字、是否引进新语法……
因为得票太分散,因而,这个 PEP 在其时被拒绝了。PEP 中写道:“Python 的一个规划原则是在不确定采纳哪条路线时,则坚持现状。”
and-or 用于条件挑选的问题
以上的投票事件发生在 2004 年 3 月,可是,在 PEP 被拒绝后,相关论题的评论并未停息,因为咱们总想找一种简练的方法来替换“if-else“。
时刻到了 2005 年 9 月,邮件组中有人提议在 Py3.0 中改变”and”与”or”操作符的逻辑,提议将”and” 和 “or” 运算符简化成始终回来布尔值,而不是回来终究一个被求值的参数。
之所以发起这个提议,原因是他运用了<condition> and <expression1> or <expression2>
的方法来完结条件判别与挑选。可是这种写法在 Python 中的行为跟有些言语并不一样,运用不慎重的话,或许会变成 Bug!
看看下面的两个比如,你觉得它们会得到什么成果呢?
a = True and True or "Python猫"
b = True and False or "Python猫"
关于<condition> and <expression1> or <expression2>
,若 condition 为假,则会直接对 expression2 求值并回来成果;若 condition 为真,则先对 expression1 求值,若也为真,则不会持续对 expression2 求值,若 expression1 不为真,则对 expression2 求值。
因而,上述比如得到的 a 是“True”,而 b 会得到“Python猫”。
本系列的《Python 为什么能支撑任意的真值判别? 》介绍过 Python 在真值判别的特别之处,运用到以上结构中,将呈现更不易察觉的问题。比如,该邮件的作者便是遇到了“expression1”为复数“0+4i”,这个数的真值判别为 False,因而导致终究回来的不是预期的“expression1”,而是“expression2”!
在没有更好的计划前,“and-or”是比较常见的条件挑选写法,PEP-308 也提及了它,也指出了当“expression1”为假的情况,还认为这种计划是丑陋和令人费解的。
这封邮件再次引发了社区对条件挑选语法的评论,大佬们纷纷登场。
以我现在的视角剖析,其实便是开发者们不满足于“if-else”的现状,可是其时流行的“and-or”写法并不够好,因而,咱们期望 Python 规划出新的规范性语法,来解决这个痛点。
异乎寻常的条件表达式
在通过 10 天的邮件评论后,Guido van Rossum 终究决定增加一个条件表达式,语法方法为X if C else Y
。因而,PEP-308 被重开和更新,并很快就在次年的 2.5 版本中完结了。
前文已提到过这个让一些人感觉不舒服的计划了,因为它没有将条件判别逻辑放在最前面。
那么,为什么终究的胜者会是它呢?这是不是最优的规划呢?
不可否认,起到决定性效果的原因是 Guido。因为社区在一年半前投票时没有形成大都意见,因而他行使 BDFL (终身仁慈独裁者)的决议计划权力,裁定出一个他认为是最佳的计划。
X if C else Y
十分易于了解,可读性高。它连续了“清晰优于隐式”的风格,运用了直观口语化的“if-else”,而不是引进或许引起混杂的标点符号,就像 Python 挑选“and”和“or”两个单词,而不是“&&”和“||”两个符号,它们有着异曲同工之妙。
尽管调整后的语法顺序让人不太习气,但其实这样的完结却大有好处。首先,它只需复用“if-else”两个关键字,而不需求引进“then”、“when”和其它语法要素,也不像(if <condition>: <expression1> else: <expression2>)
那样的繁琐。
其次,为了验证X if C else Y
的有效性,Guido 排查了规范库中所有“and-or”组合的写法,发现那些C and X or Y
写法都能够被X if C else Y
替换掉。规范库的情况,证明了这新的语法是可行的。
回忆这段前史,咱们能够梳理出一条线索:Python 没有规划三元运算符“?:”,首要是因为它不符合 Python 清晰直观的规划风格。终究选用X if C else Y
这种规划,首要的意图其实是消除“and-or”写法的隐患,这种规划简明易读,十分好用。
整体而言,Python 规划者十分垂青可读性与可维护性,不选用三元运算符而创造条件表达式语法,这是一个通过了敞开评论、慎重评价与权衡取舍的成果。
Go、Rust 为什么不支撑三元运算符?
调查完 Python 的规划原因后,咱们再来调查“反派阵营”中两门最抢手的言语。
首先是 Go 言语,官网的 FAQ 专门列出了一个问题:“Why does Go not have the ?:
operator?”。
Go 言语不支撑“?:”运算符,而是推荐运用原生的“if-else”写法。文档的解释很简短,只有一段话:
Go 言语没有 ?: 运算符,因为言语的规划者们常常看到它被用来创立难以了解的杂乱表达式。尽管 if-else 方法比较长,可是它无疑更明晰易懂。一个言语只需求一个条件操控流结构。
接着是 Rust 言语,它的官方文档中好像没有任何关于不支撑三元运算符的解释。但在查阅资料后,我发现它也有一段特别的故事,十分有意思:在 2011 年 6 月时,Rust 曾经引进过三元运算符(#565),但是半年后,规划者意识到这个特性是剩余的,因而又把它移除了(#1698、#4632)!
为什么三元运算符在 Rust 是剩余的呢?因为它的 if 语法并不像其它言语是“句子(statement)”,而是一个“表达式(expression)”,这意味着你能够直接将 if 表达式赋值给变量:
// 若条件为真,得到 5,否则 6
let number = if condition { 5 } else { 6 };
这种语法方法足够简单明了,不便是将咱们都了解的“if-else”直接用于赋值么,太方便了,替换成三元运算符的话,的确有点画蛇添足之感。
别的,Rust 运用花括号区分代码块,因而上例的花括号内能够包含多条表达式,也支撑换行,例如这个比如:
let x = 42;
let result = if x > 50 {
println!("x is greater than 50");
x * 2 // 这是一个表达式,将回来的值赋给 result
} else {
println!("x is less than or equal to 50");
x / 2 // 也是一个表达式,将回来的值赋给 result
};
这种用法,Python 是不或许做到的。最关键的差异在于,Rust 的 if 是表达式而不是句子。
这两个概念的差异是:
- 表达式(expression)一般指的是由变量、常量、运算符等组成的一个可求值的代码片段,它的求值成果能够用到其它表达式或句子中。
- 句子(statement)一般指的是完结某个使命的单个指令或一组指令,例如赋值句子、条件句子、循环句子等,它没有回来值(或许为空),不能用于赋值操作。
除了 Rust 外,还有一些编程言语中的 if 是表达式而不是句子,例如 Kotlin、Scala、F#、Swift,它们在理论上也不需求运用三元运算符。(题外话:Swift 是个破例,它也有三元运算符。Kotlin 有“?:”运算符,留意两个符号是连在一起的,val result = a ?: b
表示:假如 a
不为 null
,则赋值给 result
;否则将 b
赋给 result
)
因为有这种言语规划层面的差异,因而在面对“是否要支撑三元运算符”这个问题时,Rust 和 Python/Go 的考虑角度有着天然不同的起点。知道了这种差异后,咱们对编程言语会有更明晰地认知。
回到本文的问题:为什么有些编程言语不选用干流的三元运算符语法呢?
不可否认,“?:”的确是一种简练好用的规划,但是,标点符号的负面影响是过于笼统,可读性并不及“if-else”那样强。别的,不同言语的规划风格与运用习气,也会导致不同的挑选。
Python 在通过一番波折后,终究规划出了异乎寻常的条件表达式。Go 言语清晰表示不支撑三元运算符。Rust 先规划后舍去,首要的原因在于 if 表达式的言语基础。
调查完这三个抢手言语后,我相信你已收成了一个满足的答案。假如是这样,请点赞支撑一下本文吧!
终究,本文出自“Python为什么”系列,悉数文章已归档在 Github 上,欢迎 star 和提 issue。