当前位置:首页>python>Python 里的条件判断、循环和函数,别再一股脑塞进 main 里

Python 里的条件判断、循环和函数,别再一股脑塞进 main 里

  • 2026-09-10 08:16:18
Python 里的条件判断、循环和函数,别再一股脑塞进 main 里
一份两百多行的 Python 脚本,报错位置却永远停在最后一句:
ValueError: invalid literal for int() with base 10: ''

我第一眼不太信是数据本身的问题。

继续往上翻,果然看到一段代码:读取 CSV、判断字段、计算金额、拼接结果、记录异常,全挤在一个 for 循环里。条件判断套了四层,循环中间还夹着三个 continue。

这种代码平时跑得好好的,一旦数据脏一点,排查起来特别费劲。

Python 的条件判断、循环和函数都不复杂。真正容易写乱的,是三样东西混在一起以后,没有边界。

拿一个常见的订单导入脚本来说。CSV 中包含订单号、用户编号、商品数量和单价,我们需要过滤无效数据,再算出订单金额。

很多人第一版会这么写:

for row in rows:
if row.get("order_no"):
if row.get("quantity"):
if row.get("price"):
                quantity = int(row["quantity"])
                price = float(row["price"])
if quantity > 0and price > 0:
                    total = quantity * price
                    results.append({
"order_no": row["order_no"],
"total": total
                    })

代码能运行,但我一般不会让它进正式脚本。

最大的问题不是缩进难看,而是失败原因被吞了。订单号为空、数量格式错误、价格小于零,最后都是“没有进入 results”。等业务问某条订单为什么没导入,只能重新跑一遍,再临时加打印。

条件判断最好先处理失败分支。

defcheck_order(row):
    order_no = row.get("order_no", "").strip()
ifnot order_no:
returnFalse, "订单号为空"

try:
        quantity = int(row.get("quantity", ""))
        price = float(row.get("price", ""))
except (TypeError, ValueError):
returnFalse, "数量或单价格式错误"

if quantity <= 0:
returnFalse, "商品数量必须大于0"

if price <= 0:
returnFalse, "商品单价必须大于0"

returnTrue, {
"order_no": order_no,
"quantity": quantity,
"price": price
    }

这里没有什么炫技,就是把错误尽早返回。

我不太喜欢为了追求“只有一个 return”,硬把所有分支包在一个巨大的 if 里面。业务校验不是数学证明,哪个条件不满足,就在哪停下来,后面的人看代码也省事。

循环负责调度,不负责处理全部业务。

defload_orders(rows):
    passed = []
    rejected = []

for line_no, row in enumerate(rows, start=2):
        ok, detail = check_order(row)

ifnot ok:
            rejected.append({
"line": line_no,
"order_no": row.get("order_no", ""),
"reason": detail
            })
continue

        detail["amount"] = round(
            detail["quantity"] * detail["price"], 2
        )
        passed.append(detail)

return passed, rejected

continue 在这里很合适。当前数据不合法,记录原因,然后处理下一条。

但 continue 也不能乱用。一个循环里跳转太多,执行路径会变得很碎。尤其是 continue 前面如果还有计数、文件写入或者状态更新,很容易漏执行。

我处理循环时通常会看两件事:这次循环的目标是什么,什么情况下应该立刻结束。

比如在一批订单里查找第一个金额异常的订单,找到后就没必要继续扫:

deffind_first_abnormal(orders, limit):
for order in orders:
if order["amount"] > limit:
return order

returnNone

这里直接 return 比设置一个变量再 break 更干净。函数已经拿到结果,就结束。没有必要让代码绕一圈再出去。

另一种情况是批量处理,单条失败不能影响后面的数据。这时异常要放在循环内部,而不是包住整个循环:

defcalculate_amounts(orders):
    completed = []
    failed = []

for order in orders:
try:
            amount = order["quantity"] * order["price"]
            completed.append({
                **order,
"amount": round(amount, 2)
            })
except (KeyError, TypeError) as exc:
            failed.append({
"order_no": order.get("order_no", ""),
"error": str(exc)
            })

return completed, failed

如果把 try 写在 for 外面,一条数据出错,整批任务都停了。这种写法在测试数据里不明显,上线跑导入任务时很烦,前面处理了多少、后面还剩多少,全得重新确认。

函数也不是拆得越细越好。

我见过一个字段校验被拆成七八个函数:判断是否为空一个函数,转整数一个函数,判断正数又一个函数。调用链看着很“规范”,排查时得来回跳文件。

函数值不值得拆,我一般看它有没有独立职责。

check_order() 负责校验并转换数据,load_orders() 负责遍历和收集结果,这种边界比较自然。至于一行 quantity > 0,没必要专门包装成函数。

最后把脚本入口收一下:

defrun_import(rows):
    valid_orders, invalid_rows = load_orders(rows)

    print(
f"导入完成,成功 {len(valid_orders)} 条,"
f"失败 {len(invalid_rows)} 条"
    )

for item in invalid_rows[:10]:
        print(
f"第 {item['line']} 行失败:"
f"{item['reason']}"
        )

return valid_orders

条件判断决定当前数据能不能继续,循环决定下一条数据什么时候开始,函数负责把不同职责隔开。

三样东西单独看都简单,混在一起就很考验代码习惯。

遇到判断套循环、循环里又塞异常处理的代码,我一般不急着优化语法。先把失败分支拉出来,再把单条处理从循环里拿出去。代码层级一降,很多隐藏问题自己就露出来了。

最新文章

随机文章