还在用 if-else 堆代码?这 10 个 Python 写法能救一下
一坨 if-else 最烦的地方,不是丑。 是你改第三次的时候,已经不敢确定上一层条件还成不成立。
我见过这种代码,订单状态加一个“退款中”,优惠券再加一个“部分核销”,最后整个函数像套娃。你第一眼看着还能跑,第二眼就想打日志。
比如这种:
defcalc_fee(order):
if order["type"] == "vip":
if order["city"] == "sh":
return order["amount"] * 0.88
else:
return order["amount"] * 0.9
elif order["type"] == "new_user":
return order["amount"] - 20
elif order["type"] == "staff":
return0
else:
return order["amount"]
这种代码不是不能写,业务刚起步的时候我也写。问题是它特别容易长歪。
换成字典分发,至少先把“判断”和“动作”拆开:
defvip_fee(order):
rate = 0.88if order["city"] == "sh"else0.9
return order["amount"] * ratedefnew_user_fee(order):
return max(order["amount"] - 20, 0)
defstaff_fee(order):
return0
FEE_RULES = {
"vip": vip_fee,
"new_user": new_user_fee,
"staff": staff_fee,
}
defcalc_fee(order):
handler = FEE_RULES.get(order["type"], lambda x: x["amount"])
return handler(order)
这地方我一般不追求什么“设计模式”,先把函数拆出来。后面规则多了,至少能单独测。
第二个,能提前返回就提前返回。不要为了显得“结构完整”,把所有逻辑都塞在一个大缩进里。
defsubmit_refund(order):
if order isNone:
return"order_empty"if order["status"] != "paid":
return"status_not_allowed"
if order["refund_amount"] <= 0:
return"bad_amount"
return do_refund(order)
这种写法土,但是稳。线上排问题时,我更愿意看这种一眼能断掉的代码。
第三个,多个条件别硬拼 and、or。尤其是权限、状态、渠道这种东西。
ALLOW_STATUS = {"paid", "part_refunded"}
ALLOW_CHANNEL = {"app", "miniapp"}defcan_refund(order):
return (
order["status"] in ALLOW_STATUS
and order["channel"] in ALLOW_CHANNEL
andnot order.get("risk_locked")
)
别小看这个集合。以后产品说“PC 端也能退”,你改一行,不用进条件地狱里翻半天。
第四个,遇到多个字段校验,可以用校验器列表。这个写法我在导入 Excel、批量清洗接口参数时用得挺多。
defcheck_mobile(row):
returnNoneif row.get("mobile", "").isdigit() else"手机号格式不对"defcheck_amount(row):
returnNoneif float(row.get("amount", 0)) > 0else"金额不能小于等于0"
defcheck_name(row):
returnNoneif row.get("name") else"姓名为空"
CHECKERS = [check_name, check_mobile, check_amount]
defvalidate_row(row):
errors = []
for checker in CHECKERS:
msg = checker(row)
if msg:
errors.append(msg)
return errors
这样加规则不会破坏原来的判断。导入失败时还能直接把错误写回去。
第五个,match-case 不是炫技,适合处理结构比较固定的事件。
defhandle_event(event):
match event:
case {"type": "pay_success", "order_id": oid, "amount": amount}:
return sync_pay_result(oid, amount) case {"type": "refund_done", "order_id": oid}:
return close_refund_ticket(oid)
case {"type": "user_locked", "user_id": uid}:
return clear_user_token(uid)
case _:
return write_unknown_event(event)
我不建议所有地方都用它。但消费 MQ、处理 webhook、解析回调消息,这玩意比一堆 if event["type"] == xxx 清楚。
第六个,别把默认值判断写得太散。dict.get()、setdefault() 这种小东西,能少很多脏判断。
defcollect_by_shop(orders):
bucket = {}for order in orders:
shop_id = order["shop_id"]
bucket.setdefault(shop_id, []).append(order["order_id"])
return bucket
当然,这个地方也可以用 defaultdict:
from collections import defaultdictdefcollect_pay_amount(orders):
amount_map = defaultdict(float)
for order in orders:
if order["status"] == "paid":
amount_map[order["shop_id"]] += order["pay_amount"]
return dict(amount_map)
这种代码没什么花活,但比每次先判断 key 在不在舒服。
第七个,别在循环里写一堆标记变量。any() 和 all() 很适合处理“有没有”“是不是都满足”。
defneed_manual_review(order):
hit_risk_word = any(
word in order["remark"]
for word in ("线下转账", "补差价", "私聊")
) all_goods_ready = all(
item["stock_status"] == "ready"
for item in order["items"]
)
return hit_risk_word ornot all_goods_ready
这里可读性比你写 flag = False 然后循环里改来改去强多了。
第八个,状态值不要散落在字符串里。至少用 Enum 管一下。
from enum import StrEnumclassOrderStatus(StrEnum):
CREATED = "created"
PAID = "paid"
CLOSED = "closed"
REFUNDING = "refunding"
defcan_ship(order):
return order["status"] == OrderStatus.PAID
字符串最坑的是拼错了也不一定马上炸。refunding 写成 refound,测试数据没覆盖,线上就开始演你。
第九个,多个相似流程,别复制函数。把变化的地方作为参数塞进去。
defexport_rows(rows, pick_fields):
result = []
for row in rows:
result.append({name: getter(row) for name, getter in pick_fields.items()})
return resultORDER_EXPORT_FIELDS = {
"订单号": lambda x: x["order_no"],
"用户": lambda x: x["buyer_name"],
"实付": lambda x: round(x["pay_amount"], 2),
}
rows = export_rows(order_list, ORDER_EXPORT_FIELDS)
这类代码在后台导出里太常见了。每个导出都复制一份循环,后面改字段格式能改到怀疑人生。
第十个,实在分支很多,就别硬装优雅,直接建规则表。
RULES = [
{
"name": "大额订单",
"hit": lambda o: o["pay_amount"] >= 5000,
"tag": "big_order",
},
{
"name": "新用户高频下单",
"hit": lambda o: o["user_level"] == "new"and o["order_count_today"] >= 3,
"tag": "new_user_risk",
},
{
"name": "地址异常",
"hit": lambda o: o.get("address_score", 100) < 60,
"tag": "bad_address",
},
]defmark_order(order):
tags = []
for rule in RULES:
if rule["hit"](order):
tags.append(rule["tag"])
return tags
这种写法不一定“高级”,但它抗改。规则多了可以放配置,甚至放数据库。最关键的是,业务说“加一条判断”,你不用拆半天老代码。
Python 里不是不能写 if-else,该写还是写。 但有些代码一眼看过去就是会长胖的,那就早点拆。
我自己的习惯很简单: 一层判断能说清楚,就直接写。 两层开始犹豫。 三层还往下套,我一般就停一下,先想想是不是该换个写法了。