把 Python 内置函数用对:从数据转换到迭代与 I/O

2026-07-21 39 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

Python 的内置函数不需要导入即可使用。sum()enumerate()zip()sorted() 等函数看似基础,却直接影响代码是否简洁、清晰且符合 Python 的惯用写法。掌握它们的关键并不是背诵函数列表,而是知道每类函数解决什么问题,以及哪些边界容易踩坑。

数值计算与数据类型转换

处理数值时,优先考虑表达意图明确的内置函数:

  • abs(x) 返回绝对值。
  • round(x, ndigits) 执行舍入。
  • min()max() 查找最小值与最大值。
  • sum(iterable, start=0) 汇总可迭代对象中的数值。
  • pow(x, y) 计算幂,传入第三个参数时可执行模幂运算。
prices = [19.9, 8.5, 12.0]

print(sum(prices))
print(round(sum(prices), 2))
print(min(prices), max(prices))
print(abs(-42))
print(pow(2, 10))
print(pow(2, 10, 1000))

类型转换函数同时承担数据校验入口的角色。int()float()str()bool()list()tuple()set()dict() 可以把外部数据转换成程序期望的形式。

port_text = "8080"
port = int(port_text)

unique_tags = set(["python", "api", "python"])
ordered_tags = sorted(unique_tags)

print(port, type(port).__name__)
print(ordered_tags)

需要注意,bool("False") 的结果是 True,因为任何非空字符串都是真值。解析配置中的布尔值时,应显式比较文本,而不是直接调用 bool()

def parse_bool(value: str) -> bool:
    normalized = value.strip().lower()
    if normalized in {"true", "1", "yes", "on"}:
        return True
    if normalized in {"false", "0", "no", "off"}:
        return False
    raise ValueError(f"无法识别的布尔值:{value!r}")

print(parse_bool("yes"))
print(parse_bool("OFF"))

让迭代代码更像 Python

很多冗长循环都可以由内置函数直接表达。enumerate() 同时提供索引和值,避免手动维护计数器;zip() 按位置组合多个可迭代对象;sorted() 返回新列表,而 reversed() 提供反向迭代器。

names = ["Ada", "Linus", "Grace"]
scores = [98, 91, 96]

for position, (name, score) in enumerate(zip(names, scores), start=1):
    print(f"{position}. {name}: {score}")

ranking = sorted(
    zip(names, scores),
    key=lambda item: item[1],
    reverse=True,
)
print(ranking)

all()any() 特别适合表达批量条件:

statuses = [200, 204, 201]

all_succeeded = all(200 <= status < 300 for status in statuses)
has_server_error = any(status >= 500 for status in statuses)

print(all_succeeded)
print(has_server_error)

这里使用生成器表达式,没有预先创建完整的布尔列表。all() 遇到第一个假值就停止,any() 遇到第一个真值就停止,因此它们既清楚又支持短路求值。

还要留意 zip() 的默认行为:它会在最短的输入耗尽时停止。如果不同长度意味着数据错误,在支持 strict 参数的 Python 版本中可以使用 zip(a, b, strict=True),让长度不一致立即抛出异常。

从输入到报表:一个可运行示例

下面的脚本把类型转换、排序、聚合、枚举和输出组合成一个小型成绩报表。将内容保存为 report.py 后即可运行;也可以直接复制到 Python 解释器中。

from pathlib import Path


def build_report(rows: list[str]) -> str:
    records = []

    for line_number, row in enumerate(rows, start=1):
        name, score_text = map(str.strip, row.split(",", maxsplit=1))
        score = float(score_text)
        records.append({"name": name, "score": score, "line": line_number})

    ranked = sorted(records, key=lambda item: item["score"], reverse=True)
    average = sum(item["score"] for item in ranked) / len(ranked)

    output = [f"人数: {len(ranked)}", f"平均分: {average:.2f}"]
    for rank, item in enumerate(ranked, start=1):
        output.append(f"{rank}. {item['name']}: {item['score']:.1f}")

    return "\n".join(output)


def main() -> None:
    source = Path("scores.csv")
    if source.exists():
        rows = source.read_text(encoding="utf-8").splitlines()
    else:
        rows = ["Ada, 98", "Linus, 91", "Grace, 96"]

    if not rows:
        raise ValueError("没有可处理的成绩记录")

    print(build_report(rows))


if __name__ == "__main__":
    main()

运行命令:

python report.py

这个例子中的函数各自承担明确职责:map() 清理拆分后的字段,float() 完成类型转换,sorted() 生成排名,sum() 计算总分,len() 提供记录数量,enumerate() 生成名次,print() 输出最终结果。

实际项目还应捕获格式错误和非法分数。例如,row.split() 可能因为缺少逗号而失败,float() 也可能抛出 ValueError。不要为了“让程序继续运行”而捕获所有异常;应提供行号和原始数据,让调用方能定位问题。

I/O 函数的边界

print() 适合脚本输出、交互式调试和简单命令行程序。生产服务需要日志级别、时间戳和结构化字段时,应改用 logging 模块。

input() 始终返回字符串,因此数值输入必须显式转换:

age_text = input("请输入年龄:")
try:
    age = int(age_text)
except ValueError:
    raise SystemExit("年龄必须是整数")

print(f"明年你将是 {age + 1} 岁")

不要使用 eval(input()) 解析用户数据。eval() 会执行任意 Python 表达式,无法信任的输入可能直接变成代码执行漏洞。JSON 数据应使用 json.loads(),Python 字面量确有必要时可考虑 ast.literal_eval()

采用时检查这几件事

内置函数的价值在于缩短“机制代码”,让业务意图更明显。落地时可以检查以下几点:

  • 循环计数是否可以改为 enumerate()
  • 并行遍历是否可以改为 zip(),以及是否需要检查长度一致性。
  • 批量判断是否适合 all()any()
  • 排序逻辑是否可以集中到 sorted(..., key=...)
  • 外部输入是否在边界处完成类型转换并处理异常。
  • 是否误把变量命名为 liststrsuminput,从而遮蔽同名内置函数。
  • 数据量较大时,是否使用生成器表达式避免不必要的中间列表。

不必把每个循环都压缩成一行。内置函数应该减少认知负担;当嵌套调用开始掩盖业务规则时,拆成具名函数和中间变量通常更容易维护。


相关推荐