Python技术迷

什么是 Python 中的三元表达式?

代码评审时我看到过这种东西:

if pay_amount >= 100:
    freight = 0
else:
    freight = 8

代码没错,就是啰嗦。尤其这种只为了给一个变量赋值的判断,拉开四五行,看着像在凑版面。Python 里这类场景一般直接用三元表达式。

三元表达式长这样:

freight = 0if pay_amount >= 100else8

读法也很直接:如果 pay_amount >= 100 成立,freight 就是 0,否则就是 8。

注意顺序,Python 不是很多语言里那种 condition ? a : b。Python 的写法是:

结果1if 条件 else 结果2

这个顺序刚开始容易别扭,我第一次写也老想把条件放前面。后来记住一句就行:先写你想要的正常值,再写判断,再写兜底值。

比如订单状态展示,后台存的是数字,页面想要文案:

order_status = 1

status_text = "已支付"if order_status == 1else"未支付"

print(status_text)

输出:

已支付

这类代码我一般会用三元表达式,因为它的判断很轻,不需要绕。

再看一个稍微贴近业务的。接口返回用户等级,会员打九折,普通用户原价:

user_level = "vip"
origin_price = 268

real_price = origin_price * 0.9if user_level == "vip"else origin_price

print(real_price)

这段代码没什么花活,就是少了几行 if else。这种地方用三元表达式,代码会紧一点,也不影响阅读。

但它不是拿来炫技的。

我见过有人这么写:

tag = "高风险"if score >= 90else"中风险"if score >= 60else"低风险"

这代码能跑,但我第一眼就不太想信它。不是逻辑不对,是读起来要在脑子里回放一遍判断顺序。线上排问题的时候,最怕这种“看着很短,理解很慢”的代码。

这种我宁可写成普通 if else:

score = 73

if score >= 90:
    tag = "高风险"
elif score >= 60:
    tag = "中风险"
else:
    tag = "低风险"

多几行没关系,判断层级清楚。代码不是越短越高级,尤其是业务判断,后面改的人不骂你就不错了。

三元表达式更适合这种简单二选一。

比如清洗导入数据时,字段可能为空:

row = {
"user_id": "U10086",
"nickname": ""
}

nickname = row["nickname"] if row["nickname"] else"未填写"

print(nickname)

输出:

未填写

这里的判断不是判断 None,而是利用 Python 的真假值。空字符串、空列表、空字典、0、None,在条件里都算假。

这个地方要小心。

比如订单金额为 0,你不能随手这么写:

amount = row["amount"] if row["amount"] else99

如果 amount 合法值就是 0,这行代码会把它改成 99。这种 bug 很烦,日志里看不出异常,数据就是悄悄歪了。

更稳一点的写法:

amount = row["amount"] if row["amount"] isnotNoneelse99

判断空值就判断空值,别把 0 一起误伤了。

再来一个我平时比较常用的场景,接口兜底返回:

defbuild_user_label(user):
    name = user.get("name")
    level = user.get("level")

    display_name = name if name else"匿名用户"
    level_text = "会员"if level == "vip"else"普通用户"

returnf"{display_name}({level_text})"

print(build_user_label({"name": "", "level": "vip"}))

输出:

匿名用户(会员)

这种写法比在 return 前面堆一堆小判断要干净。

还有一种情况,三元表达式可以直接放在函数返回值里:

defneed_sms_retry(send_result):
returnTrueif send_result["code"] in ("TIMEOUT", "LIMITED") elseFalse

不过这行我一般会再改一下:

defneed_sms_retry(send_result):
return send_result["code"] in ("TIMEOUT", "LIMITED")

因为条件本身已经返回布尔值了,再包一层 True if ... else False,有点多余。代码里这种东西不少,看起来像很严谨,实际上只是绕了一圈。

三元表达式还有一个容易被忽略的点:它是表达式,不是语句。

所以它可以放在赋值右边:

timeout = 3if env == "prod"else10

也可以放在列表里:

prices = [59, 199, 20, 305]

labels = ["大额订单"if price >= 200else"普通订单"for price in prices]

print(labels)

输出:

['普通订单', '普通订单', '普通订单', '大额订单']

但别写过头。列表推导式里再塞复杂三元表达式,基本就是给自己埋雷。

我自己的使用习惯很简单:

只判断一次,只选两个值,可以用。

判断里有多个分支,不用。

判断后要执行多行逻辑,不用。

条件读起来拧巴,不用。

比如这个就不值得硬压成一行:

if retry_count > 3:
    logger.warning("任务重试次数过多,task_id=%s", task_id)
    mark_failed(task_id)
else:
    push_back_queue(task_id)

它不是“赋一个值”的问题,而是两条业务路径。强行三元表达式只会把代码写丑。

Python 的三元表达式就这点东西:

value = a if condition else b

它解决的是小判断、小赋值、小返回。

用得好,代码清爽一点。用过头,排障的时候自己先骂自己。写业务代码,别为了少三行,把下一次定位问题的时间搭进去。