还在用if-else堆代码?这10个Python技巧让你的代码优雅10倍
代码里连续 7 个 if-elif,第一眼我就不太想往下看。
不是因为它一定慢。慢不慢另说,主要是这种代码最容易在第 8 个分支来的时候,把人带沟里。
尤其是面试题里问“怎么优化 if-else”,千万别上来就背设计模式。Python 里有些写法,平时处理日志、清洗字段、接口兜底、批量任务,真能少写一半废代码。
先看这个最常见的。
if status == "paid":
action = "ship"
elif status == "refund":
action = "stop"
elif status == "closed":
action = "archive"
else:
action = "wait"
这种我一般直接换成字典映射:
status_action = {
"paid": "ship",
"refund": "stop",
"closed": "archive",
}action = status_action.get(status, "wait")
别小看这一点。分支一多,字典比 if-else 清楚太多。后面产品再加状态,你只改一行,不用在一堆判断里找位置。
第二个,函数也可以放进字典。
有些人写接口回调喜欢这么干:
if event == "user_created":
create_user(payload)
elif event == "user_deleted":
delete_user(payload)
elif event == "order_paid":
mark_order(payload)
我更愿意这么写:
defignore_event(data):
print("skip event:", data.get("event_id"))handlers = {
"user_created": create_user,
"user_deleted": delete_user,
"order_paid": mark_order,
}
handler = handlers.get(event, ignore_event)
handler(payload)
这东西在消息消费里很好用。消费者入口别堆太多业务判断,入口只负责分发,真正的处理逻辑放到函数里。出了问题,看日志也好追。
第三个,能用提前返回,就别嵌套三层。
我见过这种代码:
defimport_row(row):
if row:
if row.get("mobile"):
if row.get("amount"):
save_bill(row)
看着不长,但味道已经不对了。后面再加一个身份证校验、一个黑名单校验,马上变成楼梯。
我会改成这样:
defimport_row(row):
ifnot row:
returnifnot row.get("mobile"):
return
ifnot row.get("amount"):
return
save_bill(row)
面试的时候说这个,别只说“减少嵌套”。你要说:异常路径提前挡掉,主流程留在最下面,人看代码时不用在脑子里维护一堆缩进状态。
第四个,字段清洗别到处写 if value is None。
批量导入 Excel 时经常见这种:
if name isNone:
name = ""
else:
name = name.strip()
我一般写一个小函数,别让脏数据处理散得到处都是。
defclean_text(value):
if value isNone:
return""
return str(value).strip()name = clean_text(row.get("customer_name"))
city = clean_text(row.get("city"))
remark = clean_text(row.get("remark"))
这种代码不炫,但省命。后面你发现要统一去掉全角空格,只改一个地方。
第五个,用 or 做简单兜底,但别乱用。
timeout = config.get("timeout") or3
这行没问题,配置没有就默认 3 秒。
但如果 0 是合法值,这么写就坑了。比如限流阈值允许配置 0,表示关闭:
limit = config.get("limit") or100
这就把 0 改成 100 了。这个地方我一般老老实实写:
limit = config.get("limit")
if limit isNone:
limit = 100
优雅不是少写两行,是别把线上配置吃掉。
第六个,列表推导式适合清爽,不适合炫技。
清理一批手机号:
mobiles = []
for item in rows:
mobile = clean_text(item.get("mobile"))
if mobile:
mobiles.append(mobile)
可以写成:
mobiles = [
clean_text(item.get("mobile"))
for item in rows
if clean_text(item.get("mobile"))
]
但这里我其实不太喜欢,因为 clean_text 调了两次。更稳一点:
mobiles = []for item in rows:
mobile = clean_text(item.get("mobile"))
if mobile:
mobiles.append(mobile)
不是所有短代码都更好。面试里你可以讲这个判断:列表推导式适合简单转换,一旦有重复计算、有日志、有异常处理,就退回普通循环。
第七个,enumerate 别忘了用。
导入数据报错时,最怕日志只打印“手机号为空”,不告诉你第几行。
for index, row in enumerate(rows, start=2):
mobile = clean_text(row.get("mobile"))ifnot mobile:
print(f"第 {index} 行手机号为空")
continue
save_customer(row)
start=2 是因为 Excel 第一行通常是表头。这个细节挺实用,面试官一般也听得出来你不是在背语法。
第八个,zip 处理字段映射很顺手。
有时候拿到一行 CSV,是列表:
values = ["1001", "Tom", "paid"]
别手动一个个取:
order_id = values[0]
name = values[1]
status = values[2]
可以这样:
fields = ["order_id", "customer_name", "status"]
record = dict(zip(fields, values))
不过这里也要补一句:如果字段数量可能不一致,我会先校验。
if len(values) != len(fields):
raise ValueError(f"字段数量不对: {values}")record = dict(zip(fields, values))
zip 默认会截断,多出来的数据直接没了。这个坑不提醒,导入脚本迟早背锅。
第九个,collections.defaultdict 很适合分组。
统计每个用户的订单号:
from collections import defaultdictuser_orders = defaultdict(list)
for order in orders:
user_orders[order["user_id"]].append(order["order_id"])
不用先判断 key 在不在。
if user_id notin user_orders:
user_orders[user_id] = []
这种判断写多了很烦,而且容易把主逻辑淹没。分组、计数、归类,defaultdict 基本够用。
第十个,异常别一把梭哈 except Exception。
有些脚本这么写:
try:
sync_order(order)
except Exception:
pass
这代码我看到会有点火。它不是优雅,是把事故藏起来。
至少要把关键字段打出来:
try:
sync_order(order)
except TimeoutError as e:
print("订单同步超时:", order.get("order_id"), e)
except ValueError as e:
print("订单数据异常:", order.get("order_id"), e)
如果确实要兜底,也别静默:
except Exception as e:
print("未知异常:", order.get("order_id"), type(e).__name__, e)
线上最怕的不是报错,是错了你还不知道。
这些技巧单独看都不大,甚至有点碎。但代码质量很多时候就是这些碎地方堆出来的。
if-else 不是不能用,该用就用。两个分支写得清清楚楚,没必要硬改成花活。真正该动的是那种状态越来越多、校验越来越深、异常越来越糊的代码。
面试时也别只回答“用字典替代 if-else”。往下多说一句:状态分发用字典,异常路径提前返回,字段清洗集中处理,循环里保留日志和行号。
这比背十个语法糖管用。