一看到有人还在自己手搓一堆小工具函数,我就知道 Python 版本八成有点老,或者压根没认真翻过新版本文档。
有些代码不是不能跑,是写得费劲。明明解释器都把刀递你手里了,还在那拿指甲抠。Python 这几年加的几个内置函数,我自己用下来就一个感觉:真省事,而且省的不是那点字符数,省的是脑子。写脚本、清洗数据、做批处理的时候,手感差很多。这个成稿的味道我按你给的参考气质收了收,尽量往“真写过、真用过”的方向落。
先看 breakpoint()。
以前线上临时复现一个小问题,最烦的是加一堆 print(),删的时候还容易漏。现在我排查脚本逻辑,尤其是文件导入、字段清洗这种,一般直接断一下。
def clean_row(row):
name = row.get("name", "").strip()
age = row.get("age")
if not name:
breakpoint() # 这里直接停,看原始数据长啥样
return {
"name": name,
"age": int(age) if age else None
}
这玩意不是给你线上开着玩的,排查本地脚本特别顺手。那种“怎么这行数据又脏了”的场景,打一堆日志不如直接断进去看变量现场。
再一个,zip(..., strict=True) 我是真想早点认识它。
以前对两个列表做配对,默认 zip() 是静默截断。这个行为看着温柔,实际很坑。你以为数据一一对应,结果后面少了几条,程序也不报错,最后账对不上。
user_ids = [101, 102, 103]
scores = [88, 91]
for uid, score in zip(user_ids, scores, strict=True):
print(uid, score)
这个代码会直接抛异常。该炸就炸,别给我装正常。批量对账、导入映射、拼接请求参数时,这种“早点报错”比“默默容忍”值钱得多。
还有个容易被低估的,aiter() 和 anext()。
平时写业务接口的人可能感受不深,但你只要碰过异步消费、异步日志流、异步抓接口分页,就知道以前手动 async for 套控制逻辑有多别扭。现在可以直接取下一个。
async def fetch_pages(client):
page_no = 1
while True:
data = await client.get_page(page_no)
if not data:
break
yield data
page_no += 1
async def main(client):
it = aiter(fetch_pages(client))
first_page = await anext(it, [])
if not first_page:
return
print("第一页数据量:", len(first_page))
这个写法在“先探一页再决定后续逻辑”的场景里挺顺手。不是所有异步场景都非得一把梭 async for 到底。
还有 pairwise(),虽然它不算内置本体,是标准库 itertools 里的,但我还是想顺手提一下,因为它和新版本这波“少写废代码”的感觉是一路的。日志分析时,经常要比较相邻两次状态变化,老写索引我第一眼就嫌麻烦。
from itertools import pairwise
times = [12, 15, 21, 30]
for prev_t, curr_t in pairwise(times):
print("间隔:", curr_t - prev_t)
你看这种代码,意思是直接露出来的。以前那种 for i in range(len(times) - 1),也能写,就是不利索。
再说个我现在很常用的:open() 配合 encoding、errors 明确写死,虽然不是新增函数,但这几年 Python 生态越来越强调“别靠默认值活着”。尤其处理 CSV、日志、爬下来的文本文件时,乱码和脏字符真不少。
with open("app.log", "r", encoding="utf-8", errors="replace") as f:
for line in f:
if "timeout" in line.lower():
print(line.strip())
很多人问题不在逻辑,在输入。文件一脏,脚本就歪。这个我踩过,不展开。
最后再放一个我自己批量清洗里会写的小片段,把这些“新一点的手感”串起来:
def load_users(rows):
for idx, row in enumerate(rows, start=1):
user_id = row.get("user_id")
mobile = row.get("mobile", "").strip()
if not user_id or not mobile:
print(f"第 {idx} 行缺字段: {row}")
continue
yield user_id, mobile
users = [{"user_id": 1, "mobile": "1380000"}, {"user_id": 2}]
mobiles = ["1380000", "1390000"]
for (uid, mobile1), mobile2 in zip(load_users(users), mobiles, strict=True):
print(uid, mobile1, mobile2)
这里面其实没什么高深东西,但写起来就是顺。新版本 Python 香,很多时候不是因为它多了什么惊天功能,而是它开始认真帮你减少那些“明明很烦但以前只能忍着写”的边角料。
所以别老盯着语法糖这三个字。真正在生产里省时间的,往往就是这些小函数、小参数、小改动。脚本短一点还是其次,关键是少埋坑,少靠肉眼兜底。
有空把自己机器上的 Python 升一升,然后把以前那堆祖传工具函数翻出来看一眼。你会删掉不少。