代码里一旦开始出现 for i in range(len(xxx))、两层 if 套着判断、拿 lambda 硬拧排序,我基本就知道这段代码还能再收一收。
Python 真有些东西,用的时候不觉得,回头看旧代码会嫌弃自己:当时怎么能写那么笨。下面这 6 个内置函数,我是真觉得顺手,而且不是那种“面试题式顺手”,是你改脚本、清数据、写接口兜底、查日志时,会很自然掏出来的那种。
1. enumerate:别再自己维护下标了
我最烦这种代码:
rows = ["A100", "A101", "A102"]
i = 0
for row in rows:
print(f"第{i+1}行: {row}")
i += 1
这东西不是不能跑,就是别扭。循环的是数据,脑子里还得分一块地方去记索引,纯属给自己找事。直接上 enumerate:
rows = ["A100", "A101", "A102"]
for line_no, row in enumerate(rows, start=1):
print(f"第{line_no}行: {row}")
做导入校验时尤其顺手,报错定位非常直接:
def validate_users(rows):
errors = []
for line_no, row in enumerate(rows, start=2): # Excel表头占一行
mobile = row.get("mobile")
name = row.get("name")
if not mobile:
errors.append(f"第{line_no}行手机号为空")
continue
if len(name or "") > 20:
errors.append(f"第{line_no}行姓名长度超过20")
return errors
这种地方你要是还手动 index += 1,我第一眼就不太信这代码后面能写多利索。
2. zip:两份数据要并着走,别自己凑
线上对账、字段比对、批量拼装参数,这类场景经常会有两份列表一起处理。很多人会这么写:
user_ids = [101, 102, 103]
scores = [88, 92, 76]
for i in range(len(user_ids)):
print(user_ids[i], scores[i])
问题不大,就是土。zip 的意思很直接:你俩绑一块走。
user_ids = [101, 102, 103]
scores = [88, 92, 76]
for user_id, score in zip(user_ids, scores):
print(user_id, score)
做批量入库参数整理时很好用:
order_ids = [9001, 9002, 9003]
statuses = ["paid", "cancelled", "paid"]
payload = []
for order_id, status in zip(order_ids, statuses):
payload.append({
"order_id": order_id,
"status": status
})
再往前一步,字典都能直接拼,在处理 CSV 或接口字段映射时能极大提升开发效率:
fields = ["name", "mobile", "dept"]
values = ["张三", "13800000000", "技术部"]
user = dict(zip(fields, values))
print(user)
# {'name': '张三', 'mobile': '13800000000', 'dept': '技术部'}
有个细节要记一下:zip 默认按最短的来。也就是说两边长度不一致,多出来的那部分会被静默扔掉。所以我自己遇到核心数据拼装,通常会先看一眼长度:
if len(order_ids) != len(statuses):
raise ValueError(“订单ID和状态数量不一致”)
这种坑,晚发现不如早一点嫌弃它。
3. any:只要有一个满足,立刻停
这个函数特别适合“拦截式判断”。比如你拿到一批导入数据,想先看看有没有空手机号。很多人会先写个标志位:
has_empty_mobile = False
for row in rows:
if not row.get("mobile"):
has_empty_mobile = True
break
这就有点 Java 后遗症了。Python 里直接写:
has_empty_mobile = any(not row.get("mobile") for row in rows)
看着就干净。接口结果聚合时也常用。比如调用下游多个节点,只要有一个失败就告警:
results = [
{"host": "10.0.0.1", "ok": True},
{"host": "10.0.0.2", "ok": True},
{"host": "10.0.0.3", "ok": False},
]
if any(not item["ok"] for item in results):
print("有节点执行失败,先别往下走”)
日志关键字扫描也顺手:
keywords = ["TimeoutError", "Connection refused", "BrokenPipeError"]
if any(word in log_text for word in keywords):
print(“这段日志值得单独拎出来看”)
我平时看异常日志,第一步经常不是通读全文,而是先看有没有几个眼熟的坏味道。any 就特别适合干这个。
4. all:不是“有没有”,是“是不是全都满足”
all 和 any 正好反过来。比如你批量校验接口参数,要求每条数据都带 user_id:
if all(item.get(“user_id”) for item in payload):
print(“参数完整,可以发请求”)
else:
print(“有脏数据,别发”)
或者校验一组文件是不是都存在:
from pathlib import Path
files = [
"/data/task/a.csv",
"/data/task/b.csv",
"/data/task/c.csv",
]
if all(Path(f).exists() for f in files):
print(“依赖文件齐了”)
这个在定时任务里很常见。有些脚本启动前必须确认几份输入文件、几项配置、几个环境变量都在,不然跑一半才炸,最恶心。再比如检查返回码是不是都成功:
resp_list = [
{"code": 0, "msg": "ok"},
{"code": 0, "msg": "ok"},
{"code": 0, "msg": "ok"},
]
if all(resp["code"] == 0 for resp in resp_list):
print(“全部成功”)
这种写法一眼就知道你在干嘛,不需要读两层循环才明白意图。
5. sorted:排序别只会升序降序,key 才是正经东西
很多人用 sorted 只会写个:
nums = [5, 2, 9, 1]
print(sorted(nums))
这当然没毛病,但也没啥意思。真正在线上脚本里常用的是 key。你想按哪个字段排、按什么规则排,全在这里。比如日志按耗时倒排,先把最慢的接口揪出来:
api_logs = [
{"path": "/order/create", "cost_ms": 120},
{"path": "/user/detail", "cost_ms": 45},
{"path": "/pay/callback", "cost_ms": 860},
]
slow_logs = sorted(api_logs, key=lambda x: x[“cost_ms”], reverse=True)
print(slow_logs)
输出以后,谁慢一眼就看见了。再比如批量处理文件时,按修改时间排:
from pathlib import Path
files = list(Path("/data/export").glob("*.csv"))
files = sorted(files, key=lambda x: x.stat().st_mtime, reverse=True)
for f in files[:5]:
print(f.name)
还有个我自己挺常用的写法:排序时把空值扔最后面。
users = [
{"name": "张三", "score": 90},
{"name": "李四", "score": None},
{"name": "王五", "score": 85},
]
users = sorted(users, key=lambda x: (x["score"] is None, x["score"]))
print(users)
这个 (x[“score”] is None, x[“score”]) 看着有点绕,但真好使。业务里很多字段不是每条都有,直接排经常会炸或者顺序很怪,这种小技巧能省你一次排查。
6. getattr:动态取属性的时候,比 if-else 铺满地强太多
这玩意很多人知道,但用得不多。一旦开始写一些通用导出、动态排序、对象字段兜底,它就很顺手。比如你有个对象列表,想按传入字段排序:
class Job:
def __init__(self, job_id, status, retry_count):
self.job_id = job_id
self.status = status
self.retry_count = retry_count
jobs = [
Job(1, "running", 2),
Job(2, "failed", 5),
Job(3, “success”, 0),
]
sort_field = “retry_count”
jobs = sorted(jobs, key=lambda x: getattr(x, sort_field, 0), reverse=True)
for job in jobs:
print(job.job_id, job.retry_count)
再比如做字段兜底:
class User:
def __init__(self, name, mobile=None):
self.name = name
self.mobile = mobile
u = User(“张三”)
mobile = getattr(u, “mobile”, “”)
dept = getattr(u, “dept”, “未知部门”)
print(mobile, dept)
你要是不用 getattr,很多时候就会开始写这种代码:
if hasattr(u, “dept”):
dept = u.dept
else:
dept = “未知部门”
不是不能写,就是看着累。还有一种比较实战的场景:接口适配。老系统、新系统返回对象字段不一致,先兜底取值:
def pick_display_name(obj):
return (
getattr(obj, “nickname”, None)
or getattr(obj, “real_name”, None)
or getattr(obj, “username”, “”)
)
这个在对接历史包袱系统时真能少写很多烂判断。
以上就是六个在实战中能显著提升代码整洁度和Python开发效率的内置函数。它们不是什么高深莫测的黑魔法,而是在日常的数据处理、脚本编写和接口开发中,能让你思路更清晰、代码更短小的实用工具。多在实际场景里想想“有没有更省力的写法”,代码质量自然就上去了。如果想了解更多实战技巧和深入交流,欢迎访问云栈社区。