python中 ? : 三元表达式的使用介绍


Posted in Python onOctober 09, 2013

(1) variable = a if exper else b
(2)variable = (exper and [b] or [c])[0]
(2) variable = exper and b or c

上面三种用法都可以达到目的,类似C语言中 variable = exper ? b : c;即:如果exper表达式的值为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)

现在大部分高级语言都支持“?”这个三元运算符(ternary operator),它对应的表达式如下:condition ? value if true : value if false。很奇怪的是,这么常用的运算符python居然不支持!诚然,我们可以通过if-else语句表达,但是本来一行代码可以完成的非要多行,明显不够简洁。没关系,在python里其实还是有对应的表达方式的。

举个例子:char *ret = (x!=0) ? "True" : "False"这行代码对应的python形式就是ret = (x and "True") or "False"(很简单吧,事实上括号可以去掉)。运行时,python虚拟机会对赋值符右边的布尔表达式(注意这里并非三元表达式)求值,返回值是最后一个被分析到的值。为什么是“最后一个被分析到的”而不是表达式中“最后一个”呢?因为布尔表达式有一个短路效应,比如a or b,如果a为真那么就不会分析b了。嗯,估计现在大家差不多明白了这行python代码的原理了。如果x为真,由于字符串“True”也为真,于是返回"True",反之,x为假,那么就没必要看字符串"True"了(短路效应),直接返回"False"。

不难看出,三元运算在python中事实上可以通过借用布尔求值表达。然后,有时会有点小问题。举个例子,char *ret = x ? "" or "VAL"。根据前面的例子,我们很自然想到在python里应该这样写,ret = x and "" or "VAL"。错了!不管x的布尔求值是真还是假,ret得到的总是"VAL"。奇怪么?不奇怪,因为在python中对空字符串的布尔求值为false,这样x and ""永远都是false,所以ret得到的自然总是"VAL"了。解决这个问题有两种办法,第一种,也是我喜欢的一种,就是写成ret = not x and "VAL" or ""。第二种,麻烦一点ret=x and [""] or ["VAL"],然后每次取ret[0]作为返回值,这是因为[""]在布尔求值时值为true。

讨论一:第一种方法代码明显要简洁,效率也高,那么还有必要使用第二种么?当然,第一种办法有局限性,只有当我们非常明确其中一个值布尔求值时不可能为false时才能使用。在我们的示例中,由于"VAL"肯定返回true所以可以使用。如果是两个变量呢,像这样ret=x and val1 or val2,你就只能老老实实写成ret=x and [val1] or [val2],然后取ret[0]作为结果了。因为这行语句所表达的不是“当x为真返回val1,否则返回val2”,而是“当x为真并且val1为真返回val2,否则返回val2”。

讨论二:大家都知道python里有list和tuple,前面这行代码ret=x and [""] or ["VAL"]我们就是通过list解决,有的人可能偏爱tuple,于是就会这样写ret=x and ("") or ("VAL")。错了!这里ret[0]永远都是空字符串(在2.5上测试)。这是我比较faint的一点,为啥[""]为真而("")为假呢?

最后,附上python对典型数值的布尔求值结果,这对我们书写三元运算的等价语句很有用。

输入 布尔求值
1,-1,[“”] True
0, “”, None, [], (), {}, (“”) False

python 三元表达式

之前学习的Python提到了对于类似C语言的三元条件表达式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'

在编程中我也一直这么用了,直到有一天发现了一个有趣的技巧,那就是and-or技巧,利用条件判断的优先特性来实现三元条件判断,比如P∧Q,在Python中如果P为假,那么Python将不会继续执行Q,而直接判定整个表达式为假(P值),当然如果P为真,那就还要继续执行Q来决定整个表达式值;同样的P∨Q,如果P为真,那么就不会继续执行Q了…

其实很多编程语言在逻辑判断中都应用了这套机制,目前我接触下来的貌似VB/VBScript可能不是这么做的。有了这套机制除了在if判断中提高效率外,我们还可以额外发掘一些有趣的功能,比如下面的PHP代码:

$conn = @mysql_connect(...) or die("Failed")

如果mysql_connect成功的话将会返回resource资源句柄,如果失败的话将会返回False,等等,后面还有个or,也就是失败的话还将会继续执行or后面的die语句,于是输出了错误信息并终止后续代码的执行。

再如下面的JavaScript代码:

function getEvent(e) {
    e = e || window.event;
    return e;
}

这段代码获取的是event,假如没有给getEvent传入值(即e为undefined),或者e为NULL(两者在JavaScript条件中均代表False),e = e || window.event表达式将会把window.event赋值给e,否则e为Object对象,原表达式会蜕化为e = e赋值,也就是没有改变什么。

好了,扯了这么多,稍稍有些偏题了,下面继续聊Python的and-or技巧,可以这么说,这个技巧也是利用了逻辑判断的特殊性,貌似在真正的三元表达式if else没有出来的时候其就一直在扮演三元表达式的角色,其原型是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'

但是值得注意的是虽然表面看上去能够正常工作,其实还潜藏有不可知的风险,若我们的true_part本身就是个被Python认定为False的值,这个技巧就不可用了,我们知道空字符串就是这种情况。

>>> True and "" or "Water"
'Water'

上面的表达式其实我们期望返回空字串的,如何解决呢,我在Dive Into Python中找到了解决方案:那就是利用列表特性,因为包含空字符串的列表其表达式值仍然为True,所以我们可以用列表先包装一下,然后等表达式判断完毕后在解包:

>>> a = ""
>>> b = "Water"
>>> (True and [a] or [b])[0]
''

当然为了避免出错,我们可以将其包装为函数:

def iif(condition, true_part, false_part):
    return (condition and [true_part] or [false_part])[0]

现在Python已经在语言特性中加入三元条件表达式的支持了,那就是文章一开始介绍的if else写法,所以为了妥善起见,对于三元判断还是用新的if else特性吧,其实Python官方对于加入三元表达式语法也是讨论了很久的,可以参考《PEP 308 — Conditional Expressions》。

Python 相关文章推荐
Python实现按学生年龄排序的实际问题详解
Aug 29 Python
hmac模块生成加入了密钥的消息摘要详解
Jan 11 Python
python+ffmpeg视频并发直播压力测试
Mar 06 Python
pycharm远程linux开发和调试代码的方法
Jul 17 Python
python3模拟实现xshell远程执行liunx命令的方法
Jul 12 Python
Python求两个字符串最长公共子序列代码实例
Mar 05 Python
jupyter lab的目录调整及设置默认浏览器为chrome的方法
Apr 10 Python
Python填充任意颜色,不同算法时间差异分析说明
May 16 Python
解决Python3.8运行tornado项目报NotImplementedError错误
Sep 02 Python
如何用PyPy让你的Python代码运行得更快
Dec 02 Python
python基础之停用词过滤详解
Apr 21 Python
梳理总结Python开发中需要摒弃的18个坏习惯
Jan 22 Python
Python 文件和输入输出小结
Oct 09 #Python
Python 错误和异常小结
Oct 09 #Python
Python 命令行非阻塞输入的小例子
Sep 27 #Python
用Python脚本生成Android SALT扰码的方法
Sep 18 #Python
python pickle 和 shelve模块的用法
Sep 16 #Python
Python版的文曲星猜数字游戏代码
Sep 02 #Python
pytyon 带有重复的全排列
Aug 13 #Python
You might like
说说PHP的autoLoad自动加载机制
2012/09/27 PHP
对比PHP对MySQL的缓冲查询和无缓冲查询
2016/07/01 PHP
php 三元运算符实例详细介绍
2016/12/15 PHP
PHP实现关键字搜索后描红功能示例
2019/07/03 PHP
javascript中的几个运算符
2007/06/29 Javascript
js事件冒泡实例分享(已测试)
2013/04/23 Javascript
详解JavaScript正则表达式中的global属性的使用
2015/06/16 Javascript
AngularJS+Bootstrap实现多文件上传与管理
2016/11/08 Javascript
详解js产生对象的3种基本方式(工厂模式,构造函数模式,原型模式)
2017/01/09 Javascript
angularJS模态框$modal实例代码
2017/05/27 Javascript
jquery拖动改变div大小
2017/07/04 jQuery
jquery手机触屏滑动拼音字母城市选择器的实例代码
2017/12/11 jQuery
vue 组件 全局注册和局部注册的实现
2018/02/28 Javascript
解决layer.confirm选择完之后消息框不消失的问题
2019/09/16 Javascript
关于layui导航栏不展示下拉列表的解决方法
2019/09/25 Javascript
vue使用swiper实现左右滑动切换图片
2020/10/16 Javascript
Python与人工神经网络:使用神经网络识别手写图像介绍
2017/12/19 Python
Python实现对字典分别按键(key)和值(value)进行排序的方法分析
2018/12/19 Python
在Pycharm中对代码进行注释和缩进的方法详解
2019/01/20 Python
python文件转为exe文件的方法及用法详解
2019/07/08 Python
Flask项目中实现短信验证码和邮箱验证码功能
2019/12/05 Python
解决Pycharm中恢复被exclude的项目问题(pycharm source root)
2020/02/14 Python
浅谈Python3中print函数的换行
2020/08/05 Python
美国礼品卡交易网站:Cardpool
2018/08/27 全球购物
美国女士内衣在线折扣商店:One Hanes Place
2019/03/24 全球购物
Myprotein中国网站:欧洲畅销运动营养品牌
2021/02/11 全球购物
写clone()方法时,通常都有一行代码,是什么?
2012/10/31 面试题
应聘英语教师求职信
2014/04/24 职场文书
主题教育活动总结
2014/05/05 职场文书
个人纪律作风整改措施思想汇报
2014/10/12 职场文书
财务部岗位职责
2015/02/03 职场文书
2015年端午节活动方案
2015/05/05 职场文书
走进科学观后感
2015/06/18 职场文书
中秋联欢会主持词
2015/07/04 职场文书
php 解析非标准json、非规范json
2021/04/01 PHP
解决pytorch读取自制数据集出现过的问题
2021/05/31 Python