
一个两百行的数据导入脚本,光路径拼接、参数校验、日志打印、文件关闭就占了七八十行。
这种代码功能没多复杂,维护起来却特别烦。改一次目录,漏两个反斜杠;加个字段,初始化逻辑散在三个地方;异常分支一多,close() 到底有没有执行都得靠猜。
我现在写这类脚本,第一件事不是装第三方包,而是先翻 Python 标准库。下面这 8 个,确实能把重复代码砍掉不少。
1. pathlib:别再手拼文件路径
这种写法我看到就头疼:
report_file = base_dir + "/" + day + "/failed/" + batch_no + ".json"
Linux 上可能没事,换到 Windows、目录末尾多一个斜杠,马上开始别扭。
pathlib 直接按路径对象处理:
from pathlib import Path
work_dir = Path("/data/import") / "2026-07-22"
failed_file = work_dir / "failed" / "batch_1042.json"
failed_file.parent.mkdir(parents=True, exist_ok=True)
failed_file.write_text('{"status": "failed"}', encoding="utf-8")
创建目录、判断文件、读取内容、改后缀,全在一个对象上。脚本里只要出现三次路径拼接,我一般就换掉。
2. dataclasses:少写一堆初始化代码
导入数据时,经常要定义一个临时对象。手写 __init__、__repr__,字段一改,几个地方跟着动。
from dataclasses import dataclass, field
@dataclass(slots=True)
class ImportRow:
order_no: str
amount: int
source: str = "manual"
errors: list[str] = field(default_factory=list)
@property
def valid(self) -> bool:
return not self.errors
这里的 default_factory 要注意,别直接写 errors=[]。共享可变对象这个坑,代码短的时候反而更隐蔽。
3. defaultdict:别到处判断 key 存不存在
按门店归类失败订单,最笨的写法是:
if shop_code not in failed_orders:
failed_orders[shop_code] = []
failed_orders[shop_code].append(order_no)
换成 defaultdict:
from collections import defaultdict
failed_orders = defaultdict(list)
for row in import_rows:
if row.errors:
failed_orders[row.source].append(row.order_no)
不是少两行的问题。分支没了,后面加统计逻辑时不容易把初始化写漏。
同一个模块里的 Counter 也很好用。统计错误类型,不用自己维护累加器:
from collections import Counter
error_count = Counter(
error
for row in import_rows
for error in row.errors
)
4. itertools:批量处理别重复切片
批量写数据库时,很多代码都在重复造“分批函数”。
for start in range(0, len(records), 500):
batch = records[start:start + 500]
save_batch(batch)
Python 3.12 可以直接用 itertools.batched:
from itertools import batched
for batch in batched(records, 500):
save_batch(batch)
这里我会多看一眼 Python 版本。线上还是旧版本,就别为了少几行偷偷复制一个同名实现,后面的人会以为它真是标准库。
5. functools:缓存和函数适配别自己造轮子
配置查询、规则加载这类函数,参数相同,结果也相同,却在一批数据里反复执行。
from functools import lru_cache
@lru_cache(maxsize=256)
def load_shop_rule(shop_code: str) -> dict:
return query_rule_from_db(shop_code)
这种缓存适合稳定的小结果。返回几十兆数据、内容又经常变化,还往上套 lru_cache,那不是优化,是给内存埋雷。
partial 也能处理重复参数:
from functools import partial
write_audit = partial(
save_event,
event_type="order_import",
operator="scheduler"
)
write_audit(order_no="A1024", result="success")
6. contextlib:资源释放别靠 finally 撑着
临时切换目录、打开资源、加锁,很多人会写一层又一层 try/finally。
自定义上下文后,调用处会干净很多:
from contextlib import contextmanager
from time import perf_counter
@contextmanager
def trace_cost(task_name: str):
started = perf_counter()
try:
yield
finally:
cost = perf_counter() - started
print(f"{task_name} cost={cost:.3f}s")
with trace_cost("validate-import-file"):
validate_file("/data/inbox/orders.csv")
我比较喜欢把计时、锁释放、临时文件清理放这里。业务代码里少一层缩进,排查异常时舒服不少。
7. argparse:命令行参数不要手撸 sys.argv
定时脚本一开始通常只有一个参数,后来会增加日期、批次、是否重跑。继续按数组下标取值,很快就会出现这种东西:
retry = sys.argv[3] == "1"
换成 argparse,校验和帮助信息一起解决:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--day", required=True)
parser.add_argument("--batch-size", type=int, default=500)
parser.add_argument("--retry-failed", action="store_true")
args = parser.parse_args()
参数写错时直接报清楚,不用自己维护十几个 if。
8. logging:别再复制 print 模板
脚本刚写时用 print 没问题。等它进了定时任务,还在打印“执行成功”,出问题时基本查不到批次、订单号和异常栈。
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s"
)
logger = logging.getLogger("order_import")
try:
imported = run_import(args.day, args.batch_size)
logger.info("import finished day=%s rows=%d", args.day, imported)
except Exception:
logger.exception("import failed day=%s", args.day)
raise
日志参数别提前拼成 f-string。日志级别被关闭时,字符串照样会先计算。量小看不出来,循环里打印几万次就不是一回事了。
这 8 个库不新,也不炫。真正有用的地方,是把路径、对象初始化、分组、分批、缓存、资源释放、参数解析和日志这些边角代码收回去。
业务代码只剩业务判断,脚本才不容易越改越厚。
以上就是“8 个真正能减少重复代码的 Python 标准库!”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://www.phpxs.com/post/14416/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料