Python print() 不只是输出:格式化、转义、文件流与 logging 的边界

2026-08-25 42 预计阅读时间: 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.

预计阅读时间:9 分钟

很多 Python 初学者把 print() 当成“把内容显示在屏幕上”的简单函数。实际上,它支持多个参数,可以控制分隔符、行结束符和输出目标,还能配合字符串格式化与转义序列处理更复杂的文本输出。

不过,print() 也有明确边界:临时调试和命令行输出很适合使用它,长期运行的服务、批处理任务和生产环境程序通常应该改用 logging

先看懂 print() 的参数

print() 的常见签名如下:

print(*objects, sep=' ', end='\n', file=None, flush=False)

几个关键参数分别负责:

  • objects:要输出的一个或多个对象。
  • sep:多个对象之间的分隔符,默认是一个空格。
  • end:输出末尾追加的内容,默认是换行符 \n
  • file:输出目标,默认是标准输出 sys.stdout
  • flush:是否立即刷新输出缓冲区,默认是 False

下面的例子展示了这些参数如何组合使用:

import sys
import time

name = "Ada"
score = 96

print("name:", name, "score:", score)
print("name", name, "score", score, sep=" | ")
print("进度", end=" ")
print("完成")

print("写入标准错误流", file=sys.stderr)

for step in range(3):
    print(f"处理中: {step + 1}/3", end="\r", flush=True)
    time.sleep(0.3)

print("处理完成" + " " * 10)

flush=True 在进度显示、交互式命令行程序和长时间运行的任务中很有用。没有它时,程序可能因为缓冲机制而延迟显示输出。

字符串格式化决定输出质量

直接把多个值传给 print() 很方便,但当输出需要稳定格式时,f-string 通常更清晰:

user = "Lin"
completed = 7
total = 10
ratio = completed / total

print(f"用户 {user} 已完成 {completed}/{total},完成率 {ratio:.0%}")

输出:

用户 Lin 已完成 7/10,完成率 70%

格式说明符放在冒号后面:

  • :.2f:保留两位小数。
  • :.0%:按百分比显示并舍入到整数。
  • :>10:右对齐,宽度为 10。
  • :,:为较大的数字添加千位分隔符。

例如:

amount = 1234567.891
print(f"金额: {amount:,.2f}")

输出为:

金额: 1,234,567.89

如果输出内容来自用户输入或外部数据,应注意不要把数据误当成格式模板执行。通常将外部值放进 f-string 的表达式位置即可,不要动态拼接未经控制的格式表达式。

转义序列:让文本表达换行、制表和特殊字符

Python 字符串中的反斜杠可以引入转义序列。常用写法包括:

  • \n:换行。
  • \t:制表符。
  • \\:反斜杠本身。
  • \":双引号。
  • \':单引号。
print("第一行\n第二行")
print("字段 A\t字段 B")
print("路径示例: C:\\work\\reports")
print("他说:\"任务完成了\"")

处理 Windows 路径、正则表达式或多行文本时,原始字符串可以减少转义噪声:

pattern = r"\d{4}-\d{2}-\d{2}"
print(pattern)

原始字符串并不是“完全忽略反斜杠”的字符串。它仍然有一些语法限制,例如不能以单个反斜杠结尾。对于复杂文本,三引号字符串或专门的模板工具可能更合适。

把输出写入文件或其他流

print() 默认写到标准输出,但 file 参数允许把内容写到任意具有 write() 方法的对象中。最常见的用途是写文件:

from pathlib import Path

report_path = Path("report.txt")

with report_path.open("w", encoding="utf-8") as report:
    print("日报", file=report)
    print("状态: completed", file=report)
    print("条目数: 42", file=report)

print(f"报告已写入 {report_path}")

使用 with 打开文件可以确保代码离开代码块后正确关闭文件。对于大规模数据导出,直接使用文件对象配合 print() 很直观;如果需要 CSV、JSON 等结构化格式,应使用标准库中的 csvjson 模块,避免手动拼接产生转义和格式错误。

sys.stdoutsys.stderr 也属于不同的输出流。普通结果可以写到标准输出,错误或诊断信息可以写到标准错误:

import sys

print("正常结果")
print("诊断信息", file=sys.stderr)

这样调用程序时,可以分别重定向两类内容:

python app.py > result.txt 2> diagnostics.txt

什么时候应该使用 logging

print() 适合以下场景:

  • 一次性脚本向用户展示结果。
  • 命令行工具输出进度或最终状态。
  • 快速验证某个变量的值。
  • 教学和小型实验。

当程序需要持续运行或由其他人维护时,logging 通常更合适。它可以提供日志级别、时间信息、模块名和不同输出处理器,还能通过配置控制哪些信息应该显示。

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)

logger = logging.getLogger(__name__)

logger.debug("详细调试信息")
logger.info("任务开始")
logger.warning("输入数据少于预期")
logger.error("任务失败")

在生产代码中,日志消息应使用 logging 的参数化写法,而不是提前用 f-string 拼接:

item_count = 42
logger.info("已处理 %d 条记录", item_count)

这样日志系统可以在某个级别被禁用时避免不必要的字符串格式化,也更容易由日志处理器统一管理。

这并不意味着 print()logging 只能二选一。面向终端用户的命令行提示可以继续使用 print(),内部诊断信息则交给 logging。关键是让程序的用户输出和开发运维日志保持清晰边界。

实践时的检查清单

  • 需要多个值的简单输出时,可以使用 sepend,不必手动拼接字符串。
  • 需要对齐、金额、百分比或固定精度时,优先考虑 f-string 格式说明符。
  • 看到路径、正则或多行文本中的反斜杠时,检查是否需要原始字符串或更明确的转义。
  • 写文件时使用 with 管理文件对象,并显式指定编码。
  • 需要区分正常结果和错误信息时,考虑 sys.stdoutsys.stderr
  • 需要级别、时间戳、持久化或集中收集日志时,使用 logging
  • flush=True 应该用于确实需要立即可见的交互式输出,而不是无条件添加到所有 print() 调用中。

掌握这些边界后,print() 仍然是一个轻量而强大的工具:它负责把结果可靠地送到合适的输出流,而字符串格式化和 logging 则分别解决展示质量与程序可观测性问题。


相关推荐