用好 Python 内置函数,少写一半冗余代码

2026-05-18 29 预计阅读时间:1 分钟
来源:realpython.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:8 分钟

很多 Python 开发者习惯遇到问题先翻第三方库,却忽略了标准解释器里那几十个随时可用、无需 import 的内置函数。它们覆盖数学运算、类型转换、迭代处理和 I/O,熟练掌握后,一段十行的手写循环往往能压缩到一行——而且更易读。

数学运算:一行代码替代手写循环

sum()max()min()abs()round() 是最常用的数学类内置函数。新手容易写出这样的累加:

total = 0
for price in prices:
    total += price

直接用 sum(prices) 即可。maxmin 同理——它们还支持 key 参数,按自定义规则取极值:

# 按销量找最畅销商品
best = max(products, key=lambda p: p["sales"])

round() 的第二个参数控制小数位数,round(3.1415, 2)3.14。注意银行家舍入规则:round(2.5) 返回 2 而非 3,需要四舍五入时用 decimal.Decimal.quantize

divmod(a, b) 同时返回商和余数,在分页、时间换算场景很实用:

minutes, seconds = divmod(3723, 60)  # 62 分 3 秒

数据类型转换与检查:别再 isinstance 一把梭

int()float()str()bool() 做显式转换人人都会,但几个容易忽略的细节值得记住:

  • bool(0)bool("")bool([])bool(None) 都是 False,其余为 True——写条件判断时直接 if items:if len(items) > 0: 更 Pythonic。
  • int("0xff", 16) 支持指定进制,解析配置里的十六进制字符串不用正则。

类型检查方面,type() 返回对象类型,isinstance() 判断是否属于某类型或其子类。日常用 isinstance 就够了,type 只在需要精确匹配(不含子类)时才用。

issubclass() 判断类间继承关系,写插件注册机制时比手动查 __bases__ 清晰得多。

迭代处理:map / filter / zip 的实战场景

处理可迭代对象是内置函数最密集的区域。len()sorted()reversed()enumerate()zip()map()filter()any()all() 各有适用场景。

enumerate——遍历同时拿索引,告别 range(len(...))

for idx, name in enumerate(students, start=1):
    print(f"{idx}. {name}")

zip——并行迭代多个序列,比手动索引对齐安全得多:

ids = [101, 102, 103]
names = ["Alice", "Bob", "Carol"]
roster = dict(zip(ids, names))  # {101: 'Alice', 102: 'Bob', 103: 'Carol'}

map / filter——函数式风格处理序列。列表推导式往往更直观,但在需要复用已有函数时 map 更简洁:

# 对一批字符串统一去空格、转小写
cleaned = list(map(str.lower, map(str.strip, raw_texts)))

any / all——短路判断,避免逐个循环:

if any(log.level == "ERROR" for log in logs):
    alert_ops_team()

sorted()keyreverse 参数几乎能满足所有排序需求,不必自己实现比较函数:

# 多字段排序:先按优先级降序,再按创建时间升序
tasks_sorted = sorted(tasks, key=lambda t: (-t["priority"], t["created_at"]))

I/O 与调试:print 之外的实用工具

print()sepend 参数能省掉字符串拼接:

print(*columns, sep="|", end="\n")  # 表格风格输出

input() 读取用户输入,open() 配合上下文管理器读写文件——这是基础,但 repr() 常被忽略。调试时 repr(obj) 给出带类型的明确表示,str(obj) 倾向可读表示。日志里用 repr 能避免 "None"None 混淆。

format() 和 f-string 共享同一套格式规范:

format(0.85, ".2%")  # '85.00%'
f"{0.85:.2%}"         # 同样结果,更直观

实战:用内置函数重构一段真实代码

下面是一段模拟的业务代码——从日志列表中筛选错误、汇总统计、格式化输出。先用"手写一切"的风格,再用内置函数重构。

重构前:

logs = [
    {"level": "INFO",  "msg": "start",   "ts": 1},
    {"level": "ERROR", "msg": "disk full", "ts": 5},
    {"level": "ERROR", "msg": "timeout",  "ts": 8},
    {"level": "INFO",  "msg": "end",     "ts": 12},
]

errors = []
for log in logs:
    if log["level"] == "ERROR":
        errors.append(log)

count = 0
for e in errors:
    count += 1

first_ts = None
for e in errors:
    if first_ts is None or e["ts"] < first_ts:
        first_ts = e["ts"]

last_ts = None
for e in errors:
    if last_ts is None or e["ts"] > last_ts:
        last_ts = e["ts"]

msgs = []
for e in errors:
    msgs.append(e["msg"])

report = "Errors: " + str(count) + ", from " + str(first_ts) + " to " + str(last_ts) + " — " + " | ".join(msgs)
print(report)

重构后:

logs = [
    {"level": "INFO",  "msg": "start",   "ts": 1},
    {"level": "ERROR", "msg": "disk full", "ts": 5},
    {"level": "ERROR", "msg": "timeout",  "ts": 8},
    {"level": "INFO",  "msg": "end",     "ts": 12},
]

errors = [log for log in logs if log["level"] == "ERROR"]

count   = len(errors)
first_ts = min(e["ts"] for e in errors)
last_ts  = max(e["ts"] for e in errors)
msgs    = [e["msg"] for e in errors]

print(f"Errors: {count}, from {first_ts} to {last_ts}{' | '.join(msgs)}")

输出均为:

Errors: 2, from 5 to 8 — disk full | timeout

行数从 22 行降到 9 行,逻辑更清晰:筛选用推导式,计数用 len,极值用 min/max,拼接用 f-string 和 join。每个操作对应一个内置函数,阅读时不需要追踪循环状态变量。

选用建议与避坑清单

  • 优先用内置函数,再考虑列表推导式,最后才写显式循环——这三层优先级能让代码越来越 Pythonic。
  • map/filter vs 推导式:简单变换用推导式更易读;需要复用已有命名函数时 map 更简洁。别在同一项目里混用两种风格。
  • sorted 返回新列表,reversed 返回迭代器——对大序列用 reversed 可以省内存,但只能遍历一次。
  • zip 在不等长序列上会截断。需要完整配对时用 itertools.zip_longest
  • round 的银行家舍入在财务场景会出意外,换 Decimal.quantize
  • any/all 是短路的——找到第一个 True/False 就停,别在生成器里放有副作用的操作。

内置函数不需要安装、不需要导入、不需要版本兼容顾虑。花一小时把 dir(__builtins__) 里的名字过一遍,比学一个新框架的回报更直接。


相关推荐