当前位置:首页>python>这 8 个 Python 标准库,真能把重复代码砍掉一截

这 8 个 Python 标准库,真能把重复代码砍掉一截

  • 2026-09-08 21:56:07
这 8 个 Python 标准库,真能把重复代码砍掉一截
一个脚本里同时出现 os.path.join()、字典判空、手写参数解析,再塞上几段几乎一样的 try...finally,这种代码我第一眼就不太想改。

不是不能跑。

是改一次容易漏三处,过两个月连作者自己都不敢动。

Python 标准库里有不少东西,作用不是让代码显得高级,而是把那些每天重复写、还特别容易写错的边角活直接收走。

1. pathlib:路径别再拿字符串硬拼

批量清理空日志时,没必要在 os.path 里来回套娃。

from pathlib import Path

log_dir = Path("/srv/order-service/logs")

for log_file in log_dir.glob("*.log"):
if log_file.stat().st_size == 0:
        log_file.unlink()

路径拼接、文件判断、遍历、删除,全在同一种对象上完成。

我现在看到这种代码:

base_dir + "/" + date + "/" + filename

一般都会顺手改掉。Windows、Linux 路径分隔符不同只是一个问题,更麻烦的是字符串拼接根本表达不出“这是一个路径”。

2. collections:少写一堆字典初始化

按渠道归集失败订单,很多人会先判断 key 在不在。

from collections import Counter, defaultdict

orders_by_channel = defaultdict(list)

for row in import_rows:
    orders_by_channel[row["channel"]].append(row["order_id"])

failed_reasons = Counter(
    row["reason"]
for row in import_rows
if row["status"] == "failed"
)

defaultdict 省掉反复初始化列表,Counter 省掉手写计数。

这种逻辑看着简单,业务一多,代码里很快就全是:

if key notin result:
    result[key] = []

没必要。

3. dataclasses:数据对象别再手写样板代码

导入任务的结果对象,经常只有几个字段,却要写初始化、比较、打印。

from dataclasses import dataclass, field

@dataclass(slots=True)
classImportResult:
    filename: str
    total: int = 0
    success: int = 0
    errors: list[str] = field(default_factory=list)

    @property
deffailed(self) -> int:
return self.total - self.success

这里有个坑,列表默认值别直接写成 []。共享可变对象的问题,线上批处理脚本里我见过不止一次,排查时还很绕。

4. enum:状态值别散落在代码里

任务状态一旦到处写 "running"、"failed",迟早会有人拼成 "fail"。

from enum import Enum

classTaskState(str, Enum):
    PENDING = "pending"
    RUNNING = "running"
    DONE = "done"
    FAILED = "failed"


defcan_retry(state: TaskState) -> bool:
return state is TaskState.FAILED

枚举减少的不只是重复字符串,还把状态范围收紧了。代码补全能提示,重构也更踏实。

5. contextlib:清理动作统一放回出口

统计每个处理步骤的耗时,最烦的是每段代码都写一次开始时间和日志。

import logging
from contextlib import contextmanager
from time import perf_counter

logger = logging.getLogger(__name__)

@contextmanager
defrecord_cost(step: str):
    started = perf_counter()
try:
yield
finally:
        cost_ms = (perf_counter() - started) * 1000
        logger.info("step=%s cost_ms=%.1f", step, cost_ms)


with record_cost("validate_rows"):
    validate_rows(import_rows)

异常有没有抛出,耗时日志都会打出来。

资源释放、临时文件清理、事务收尾,也是同一个思路。别在三个分支里复制相同的关闭代码。

6. functools:重复计算直接缓存

规则文件在一个进程里可能被读取几十次,内容没变,就别反复解析。

import json
from functools import lru_cache
from pathlib import Path

@lru_cache(maxsize=16)
defload_rules(rule_file: str) -> dict:
    path = Path(rule_file)
return json.loads(path.read_text(encoding="utf-8"))

lru_cache 适合输入确定、结果稳定的函数。

数据库查询、带当前时间的计算不要随便套。缓存代码不难写,缓存失效才难处理,这地方别贪。

7. itertools:列表处理别套三层循环

多个批次返回了一组订单号,需要合并去重:

from itertools import chain

batch_order_ids = [
    ["A1001", "A1002"],
    ["A1002", "A1003"],
    ["A1004"],
]

unique_ids = set(chain.from_iterable(batch_order_ids))

chain.from_iterable() 比手写嵌套循环更直接,也不用先制造一个巨大的中间列表。

标准库里这类迭代工具很多,但我不建议为了少两行代码硬用。看一眼说不清在干什么的写法,省下来的通常不是维护成本。

8. argparse:脚本参数别自己拆 sys.argv

线上补数据脚本最怕参数位置传错。

import argparse

parser = argparse.ArgumentParser(description="重跑指定日期的失败订单")
parser.add_argument("--date", required=True)
parser.add_argument("--limit", type=int, default=500)
parser.add_argument("--dry-run", action="store_true")

args = parser.parse_args()

print(args.date, args.limit, args.dry_run)

参数校验、默认值、帮助信息都处理了。

比起手写:

date = sys.argv[1]
limit = int(sys.argv[2])

多不了几行,但脚本交给别人执行时,少出很多低级问题。

这些标准库不算冷门,也没有什么炫技成分。真正有用的地方,是把路径处理、状态约束、资源回收、参数校验这些重复劳动收拢到固定写法里。

业务代码已经够容易变了,边角代码就别再一遍遍手搓。

最新文章

随机文章