python中 ? : 三元表達(dá)式的使用介紹
(1) variable = a if exper else b
(2)variable = (exper and [b] or [c])[0]
(2) variable = exper and b or c
上面三種用法都可以達(dá)到目的,類似C語言中 variable = exper ? b : c;即:如果exper表達(dá)式的值為true則variable = b,否則,variable = c
例如:
a,b=1,2
max = (a if a > b else b)
max = (a > b and [a] or [b])[0] #list
max = (a > b and a or b)
現(xiàn)在大部分高級語言都支持“?”這個三元運算符(ternary operator),它對應(yīng)的表達(dá)式如下:condition ? value if true : value if false。很奇怪的是,這么常用的運算符python居然不支持!誠然,我們可以通過if-else語句表達(dá),但是本來一行代碼可以完成的非要多行,明顯不夠簡潔。沒關(guān)系,在python里其實還是有對應(yīng)的表達(dá)方式的。
舉個例子:char *ret = (x!=0) ? "True" : "False"這行代碼對應(yīng)的python形式就是ret = (x and "True") or "False"(很簡單吧,事實上括號可以去掉)。運行時,python虛擬機會對賦值符右邊的布爾表達(dá)式(注意這里并非三元表達(dá)式)求值,返回值是最后一個被分析到的值。為什么是“最后一個被分析到的”而不是表達(dá)式中“最后一個”呢?因為布爾表達(dá)式有一個短路效應(yīng),比如a or b,如果a為真那么就不會分析b了。嗯,估計現(xiàn)在大家差不多明白了這行python代碼的原理了。如果x為真,由于字符串“True”也為真,于是返回"True",反之,x為假,那么就沒必要看字符串"True"了(短路效應(yīng)),直接返回"False"。
不難看出,三元運算在python中事實上可以通過借用布爾求值表達(dá)。然后,有時會有點小問題。舉個例子,char *ret = x ? "" or "VAL"。根據(jù)前面的例子,我們很自然想到在python里應(yīng)該這樣寫,ret = x and "" or "VAL"。錯了!不管x的布爾求值是真還是假,ret得到的總是"VAL"。奇怪么?不奇怪,因為在python中對空字符串的布爾求值為false,這樣x and ""永遠(yuǎn)都是false,所以ret得到的自然總是"VAL"了。解決這個問題有兩種辦法,第一種,也是我喜歡的一種,就是寫成ret = not x and "VAL" or ""。第二種,麻煩一點ret=x and [""] or ["VAL"],然后每次取ret[0]作為返回值,這是因為[""]在布爾求值時值為true。
討論一:第一種方法代碼明顯要簡潔,效率也高,那么還有必要使用第二種么?當(dāng)然,第一種辦法有局限性,只有當(dāng)我們非常明確其中一個值布爾求值時不可能為false時才能使用。在我們的示例中,由于"VAL"肯定返回true所以可以使用。如果是兩個變量呢,像這樣ret=x and val1 or val2,你就只能老老實實寫成ret=x and [val1] or [val2],然后取ret[0]作為結(jié)果了。因為這行語句所表達(dá)的不是“當(dāng)x為真返回val1,否則返回val2”,而是“當(dāng)x為真并且val1為真返回val2,否則返回val2”。
討論二:大家都知道python里有l(wèi)ist和tuple,前面這行代碼ret=x and [""] or ["VAL"]我們就是通過list解決,有的人可能偏愛tuple,于是就會這樣寫ret=x and ("") or ("VAL")。錯了!這里ret[0]永遠(yuǎn)都是空字符串(在2.5上測試)。這是我比較faint的一點,為啥[""]為真而("")為假呢?
最后,附上python對典型數(shù)值的布爾求值結(jié)果,這對我們書寫三元運算的等價語句很有用。
輸入 | 布爾求值 |
1,-1,[“”] | True |
0, “”, None, [], (), {}, (“”) | False |
python 三元表達(dá)式
之前學(xué)習(xí)的Python提到了對于類似C語言的三元條件表達(dá)式condition ? true_part : false_part,雖然Python沒有三目運算符(?:),但也有類似的替代方案,那就是true_part if condition else false_part。
>>> 1 if True else 0
1
>>> 1 if False else 0
0
>>> "Fire" if True else "Water"
'Fire'
>>> "Fire" if False else "Water"
'Water'
在編程中我也一直這么用了,直到有一天發(fā)現(xiàn)了一個有趣的技巧,那就是and-or技巧,利用條件判斷的優(yōu)先特性來實現(xiàn)三元條件判斷,比如P∧Q,在Python中如果P為假,那么Python將不會繼續(xù)執(zhí)行Q,而直接判定整個表達(dá)式為假(P值),當(dāng)然如果P為真,那就還要繼續(xù)執(zhí)行Q來決定整個表達(dá)式值;同樣的P∨Q,如果P為真,那么就不會繼續(xù)執(zhí)行Q了…
其實很多編程語言在邏輯判斷中都應(yīng)用了這套機制,目前我接觸下來的貌似VB/VBScript可能不是這么做的。有了這套機制除了在if判斷中提高效率外,我們還可以額外發(fā)掘一些有趣的功能,比如下面的PHP代碼:
$conn = @mysql_connect(...) or die("Failed")
如果mysql_connect成功的話將會返回resource資源句柄,如果失敗的話將會返回False,等等,后面還有個or,也就是失敗的話還將會繼續(xù)執(zhí)行or后面的die語句,于是輸出了錯誤信息并終止后續(xù)代碼的執(zhí)行。
再如下面的JavaScript代碼:
function getEvent(e) {
e = e || window.event;
return e;
}
這段代碼獲取的是event,假如沒有給getEvent傳入值(即e為undefined),或者e為NULL(兩者在JavaScript條件中均代表False),e = e || window.event表達(dá)式將會把window.event賦值給e,否則e為Object對象,原表達(dá)式會蛻化為e = e賦值,也就是沒有改變什么。
好了,扯了這么多,稍稍有些偏題了,下面繼續(xù)聊Python的and-or技巧,可以這么說,這個技巧也是利用了邏輯判斷的特殊性,貌似在真正的三元表達(dá)式if else沒有出來的時候其就一直在扮演三元表達(dá)式的角色,其原型是condition and true_part or false_part,下面舉幾個例子:
>>> True and 1 or 0
1
>>> False and 1 or 0
0
>>> True and "Fire" or "Water"
'Fire'
>>> False and "Fire" or "Water"
'Water'
但是值得注意的是雖然表面看上去能夠正常工作,其實還潛藏有不可知的風(fēng)險,若我們的true_part本身就是個被Python認(rèn)定為False的值,這個技巧就不可用了,我們知道空字符串就是這種情況。
>>> True and "" or "Water"
'Water'
上面的表達(dá)式其實我們期望返回空字串的,如何解決呢,我在Dive Into Python中找到了解決方案:那就是利用列表特性,因為包含空字符串的列表其表達(dá)式值仍然為True,所以我們可以用列表先包裝一下,然后等表達(dá)式判斷完畢后在解包:
>>> a = ""
>>> b = "Water"
>>> (True and [a] or [b])[0]
''
當(dāng)然為了避免出錯,我們可以將其包裝為函數(shù):
def iif(condition, true_part, false_part):
return (condition and [true_part] or [false_part])[0]
現(xiàn)在Python已經(jīng)在語言特性中加入三元條件表達(dá)式的支持了,那就是文章一開始介紹的if else寫法,所以為了妥善起見,對于三元判斷還是用新的if else特性吧,其實Python官方對于加入三元表達(dá)式語法也是討論了很久的,可以參考《PEP 308 — Conditional Expressions》。
相關(guān)文章
python機器基礎(chǔ)邏輯回歸與非監(jiān)督學(xué)習(xí)
這篇文章主要為大家介紹了python機器基礎(chǔ)邏輯回歸與非監(jiān)督的學(xué)習(xí)講解u,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11人工智能學(xué)習(xí)Pytorch進(jìn)階操作教程
這篇文章主要為大家介紹了人工智能學(xué)習(xí)Pytorch進(jìn)階操作的詳解教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11OpenCV連通域數(shù)量統(tǒng)計學(xué)習(xí)示例
這篇文章主要為大家介紹了OpenCV連通域數(shù)量統(tǒng)計示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Python調(diào)用騰訊API實現(xiàn)人臉身份證比對功能
這篇文章主要介紹了Python調(diào)用騰訊API進(jìn)行人臉身份證比對,簡單介紹了調(diào)用騰訊云API步驟,通過完整代碼展示與結(jié)果,需要的朋友可以參考下2022-04-04如何基于matlab相機標(biāo)定導(dǎo)出xml文件
這篇文章主要介紹了如何基于matlab相機標(biāo)定導(dǎo)出xml文件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11Python將視頻或者動態(tài)圖gif逐幀保存為圖片的方法
本文是基于opencv將視頻和動態(tài)圖gif保存為圖像幀的方法,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友參考下吧2019-09-09