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爬虫之HTTP异常处理
Nov 05 Python
python中模块的__all__属性详解
Oct 26 Python
在python中利用opencv简单做图片比对的方法
Jan 24 Python
pyqt5让图片自适应QLabel大小上以及移除已显示的图片方法
Jun 21 Python
PyTorch: 梯度下降及反向传播的实例详解
Aug 20 Python
Django 实现外键去除自动添加的后缀‘_id’
Nov 15 Python
Matplotlib绘制雷达图和三维图的示例代码
Jan 07 Python
Python爬虫实现模拟点击动态页面
Mar 05 Python
解决Jupyter Notebook使用parser.parse_args出现错误问题
Apr 20 Python
python,Java,JavaScript实现indexOf
Sep 09 Python
python海龟绘图之画国旗实例代码
Nov 11 Python
Python基础教程,Python入门教程(超详细)
Jun 24 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程序之die调试法 快速解决错误
2009/09/17 PHP
Centos PHP 扩展Xchche的安装教程
2016/07/09 PHP
tp5(thinkPHP5)操作mongoDB数据库的方法
2018/01/20 PHP
PHP curl批处理及多请求并发实现方法分析
2018/08/15 PHP
基于php解决json_encode中文UNICODE转码问题
2020/11/10 PHP
jquery插件制作教程 txtHover
2012/08/17 Javascript
下拉列表select 由左边框移动到右边示例
2013/12/04 Javascript
jquery做的一个简单的屏幕锁定提示框
2014/03/26 Javascript
jQuery插件Tmpl的简单使用方法
2015/04/27 Javascript
在JavaScript中处理字符串之fontcolor()方法的使用
2015/06/08 Javascript
js代码实现点击按钮出现60秒倒计时
2021/01/28 Javascript
极易被忽视的javascript面试题七问七答
2016/02/15 Javascript
深入浅析JSON.parse()、JSON.stringify()和eval()的作用详解
2016/04/03 Javascript
JavaScript记录光标在编辑器中位置的实现方法
2016/04/22 Javascript
JavaScript面向对象继承原理与实现方法分析
2018/08/09 Javascript
vue2.0 如何在hash模式下实现微信分享
2019/01/22 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
2019/01/27 Javascript
解决Layui当中的导航条动态添加后渲染失败的问题
2019/09/25 Javascript
vue实现员工信息录入功能
2020/06/11 Javascript
vue使用echarts实现水平柱形图实例
2020/09/09 Javascript
[01:11]steam端dota2实名认证操作流程视频
2021/03/11 DOTA
python笔记(1) 关于我们应不应该继续学习python
2012/10/24 Python
利用python代码写的12306订票代码
2015/12/20 Python
Python实现爬虫抓取与读写、追加到excel文件操作示例
2018/06/27 Python
python实现图像检索的三种(直方图/OpenCV/哈希法)
2019/08/08 Python
Django项目中实现使用qq第三方登录功能
2019/08/13 Python
python缩进长度是否统一
2020/08/02 Python
Python用SSH连接到网络设备
2021/02/18 Python
python实现简单的学生管理系统
2021/02/22 Python
canvas绘图按照contain或者cover方式适配并居中显示
2019/02/18 HTML / CSS
Wedgwood美国官网:英国骨瓷,精美礼品及家居装饰
2018/02/17 全球购物
用Java语言将一个键盘输入的数字转化成中文输出
2013/01/25 面试题
文员岗位职责
2013/11/09 职场文书
新农村建设标语
2014/06/24 职场文书
Oracle安装TNS_ADMIN环境变量设置参考
2021/11/01 Oracle
Vue全局事件总线你了解吗
2022/02/24 Vue.js