Python 的三元表达式,别拿它硬省那两行 if
代码里看到这种东西,我一般会多看一眼:
pay_status = "PAID"if order["paid_at"] else"WAIT_PAY"
这行没毛病。
但如果它后面又套了两个 if else,我就不太信了。Python 的三元表达式是个好东西,但它不是给人炫技用的。用好了,代码干净;用歪了,排查线上问题时能把自己绕进去。
Python 里的三元表达式,准确点叫条件表达式。
格式就这一种:
结果1if 条件 else 结果2
条件成立,返回结果1;条件不成立,返回结果2。
换成普通 if else,大概是这样:
if condition:
value = result_a
else:
value = result_b
用三元表达式就是:
value = result_a if condition else result_b
别看只是少了几行,味道不一样。普通 if else 更像在做流程控制;三元表达式更像在算一个值。
比如接口返回订单状态,代码里经常会有这种判断:
defbuild_order_view(order):
status_text = "已支付"if order.get("paid_at") else"待支付"
return {
"order_no": order["order_no"],
"amount": order["amount"],
"status": status_text,
}
这个地方用三元表达式很舒服,因为它只是在给 status_text 赋一个值。
如果写成下面这样,也不是不能用,就是有点啰嗦:
if order.get("paid_at"):
status_text = "已支付"
else:
status_text = "待支付"
我平时判断能不能用三元表达式,就看一点:这个判断是不是只为了得到一个值。
是,就可以用。
不是,老老实实写 if else。
比如下面这种就别硬塞:
msg = send_sms(user) if user["phone"] else write_warn_log(user)
这种代码我第一眼会怀疑它有副作用。send_sms 是发短信,write_warn_log 是写日志,这已经不是简单取值了。以后查问题时,光看这一行就得来回确认哪个函数会执行,哪个函数不会执行。
拆开更稳:
if user["phone"]:
send_sms(user)
else:
write_warn_log(user)
三元表达式还有一个细节:它只会执行被选中的那一边。
这点有时候挺有用,比如做兜底值:
defget_display_name(user):
return user["nickname"].strip() if user.get("nickname") elsef"user_{user['id']}"
nickname 有值,才会执行 .strip();没值,就走后面的默认用户名。
但这里也有坑。
Python 里空字符串、0、空列表、None 都会被当成 False。你以为自己判断的是“有没有传值”,实际可能把合法的 0 也干掉了。
比如优惠金额:
discount = order.get("discount")
discount_text = f"-{discount}元"if discount else"无优惠"
如果 discount = 0,这里显示“无优惠”,看起来还行。
但如果业务要求必须显示 -0元,这代码就错了。虽然这种需求不多,但线上真遇到过类似的,尤其是金额、库存、积分这些字段,0 往往不是“没有”。
更稳一点:
discount = order.get("discount")
discount_text = f"-{discount}元"if discount isnotNoneelse"无优惠"
这种判断我更放心,因为它明确区分了 None 和 0。
还有一种常见写法,处理配置默认值:
timeout = config.get("timeout") if config.get("timeout") isnotNoneelse3
这个比下面这种靠谱:
timeout = config.get("timeout") or3
or 3 看着很短,但如果用户配置了 timeout = 0,它会直接变成 3。这个问题藏得比较深,压测或者灰度时才容易冒出来。
三元表达式最容易写脏的地方,是嵌套。
比如有人喜欢这么写:
level = "S"if score >= 90else"A"if score >= 80else"B"if score >= 60else"C"
能运行。
但我不喜欢。
这种代码读起来要在脑子里补括号,补执行顺序。评分规则以后再加一个等级,基本就开始难看了。
我一般会改成这样:
defget_score_level(score):
if score >= 90:
return"S"
if score >= 80:
return"A"
if score >= 60:
return"B"
return"C"
行数多了,但排查时不用猜。业务规则一多,清晰比短重要。
三元表达式适合这种短判断:
log_level = "ERROR"if failed_count > 0else"INFO"
也适合这种字段清洗:
city = row["city"].strip() if row.get("city") else"未知"
还适合这种简单兜底:
page_size = req["page_size"] if req.get("page_size") in (10, 20, 50) else10
但别把它写成压缩饼干:
result = "retry"if err and err.code in retry_codes else"ignore"if err else"success"
这种我宁愿多写几行:
ifnot err:
result = "success"
elif err.code in retry_codes:
result = "retry"
else:
result = "ignore"
三元表达式还有个容易被忽略的点:它是表达式,所以可以直接放在函数参数里。
比如打印一条排障日志:
defwrite_import_log(file_name, total, failed):
level = "WARN"if failed else"INFO"
print(f"[{level}] file={file_name}, total={total}, failed={failed}")
也可以直接写:
print(f"[{'WARN'if failed else'INFO'}] file={file_name}, total={total}, failed={failed}")
这一行还能接受。
但再复杂一点,我就会先提变量。日志这种东西,真出问题的时候是给人看的,不是给解释器看的。
还有人会问,Python 有没有像别的语言那种:
condition ? a : b
没有。
Python 就是:
a if condition else b
顺序一开始看着有点别扭,因为条件在中间。写多了也还好。它的好处是读起来接近一句话:
price = vip_price if user["vip"] else normal_price
用户是 VIP,就用 VIP 价,否则用普通价。
这个语义挺顺。
我对三元表达式的使用习惯很简单:
只做赋值,别塞流程。
只放短逻辑,别塞规则。
只处理纯值,别塞一堆有副作用的函数调用。
代码不是越短越高级。尤其是 Python,已经够短了,再硬压一层,省下的两行代码,后面可能要用半小时日志去还。