一、先认识一下两位主角
(一)print
print 可以说是 Python 中最简单的输出方式了,几乎所有学过一点 Python 的人都会用。它的作用很单纯,就是把你想查看的内容直接输出到控制台。比如说,你想看看一个变量的值,直接写 print(变量名) 就行。
举个超简单的例子,假设我们有一段计算两个数之和的代码:
a = 10
b = 20
c = a + b
print("a + b 的和是:", c)
运行这段代码,控制台就会输出 a + b 的和是: 30。很直观吧,这就是 print 的基本用法。
(二)logging
logging 是 Python 内置的一个日志模块,相比 print,它的功能可要丰富得多。它可以让我们记录不同级别的日志,比如调试信息、普通信息、警告信息、错误信息等等,而且还能把日志输出到文件中,方便我们后续查看和分析。
同样用上面的例子,我们用 logging 来实现一下:
import logging
# 基本配置,设置日志级别为DEBUG,格式包含时间、日志级别、消息
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
a = 10
b = 20
c = a + b
logging.info("a + b 的和是:%d", c)
运行后,控制台会输出类似 2025 - 05 - 29 10:00:00 - INFO - a + b 的和是:30 这样的内容,不仅有我们想要的信息,还有时间和日志级别等额外信息。
二、print 的那些事儿
(一)print 的优点
- 简单易用:不需要导入任何模块,直接就能使用,对于新手来说,几乎没有学习成本。刚学编程的时候,大家肯定都用过 print 来查看变量值,快速定位问题。
- 即时反馈:只要运行代码,就能马上在控制台看到输出结果,对于一些简单的调试场景,非常方便。比如在一个循环里,想看看每次循环变量的值有没有变化,直接在循环里加个 print,马上就能看到。
(二)print 的缺点
- 输出混乱:当代码中到处都是 print 语句时,控制台的输出会变得非常混乱,尤其是在处理复杂逻辑或者大量数据的时候,很难快速找到自己需要的信息。比如说,一个函数里有多个 print,调用这个函数几次后,控制台的输出就会一大堆,看得人眼花缭乱。
- 功能单一:它只能把信息输出到控制台,不能记录到文件中,而且也无法区分日志的级别,所有的输出都是一样的 “待遇”。如果我们想在程序运行结束后,回顾一下运行过程中的信息,print 就做不到了,因为控制台的输出一旦关闭,就很难再找回来。
- 难以控制:当我们调试完代码后,需要把这些 print 语句删除或者注释掉,否则它们会一直输出,影响程序的运行效率和可读性。但是删除的时候又怕删错,毕竟有些 print 可能在关键的逻辑里,删了之后万一出问题,再找回来就麻烦了。
三、logging 全解析
(一)logging 的日志级别
logging 定义了五种日志级别,从低到高依次是:
日志级别 | 说明 |
DEBUG | 详细的调试信息,通常只在开发阶段使用 |
INFO | 程序正常运行的信息 |
WARNING | 警告信息,表示程序可能存在潜在的问题 |
ERROR | 错误信息,表示程序发生了错误 |
CRITICAL | 严重错误信息,表示程序无法继续运行 |
我们可以根据不同的场景,选择合适的日志级别。比如在开发阶段,我们可以使用 DEBUG 级别来输出详细的调试信息;在生产环境中,可能只需要 INFO 级别以上的日志,来了解程序的运行状态。
(二)logging 的优点
- 灵活的日志级别控制:我们可以通过设置日志级别,来决定哪些日志会被输出。比如设置日志级别为 INFO,那么 DEBUG 级别的日志就不会被输出了。这样我们可以在不同的环境中,灵活地控制输出的日志内容。比如在开发时,想看到所有详细的调试信息,就把级别设为 DEBUG;上线后,只想看正常运行的信息,就设为 INFO。
- 丰富的输出格式:我们可以自定义日志的输出格式,包含时间、日志级别、模块名、函数名等信息,方便我们更好地定位问题。比如上面的例子中,我们设置的格式 %(asctime)s - %(levelname)s - %(message)s,就包含了时间、日志级别和我们自己定义的消息。
- 强大的输出控制:logging 不仅可以把日志输出到控制台,还可以输出到文件中。我们可以设置不同的处理器(Handler),来实现不同的输出目标。比如同时输出到控制台和文件,或者根据日志级别输出到不同的文件中。而且,我们还可以对日志文件进行一些配置,比如设置日志文件的大小、备份数量等,避免日志文件过大占用太多磁盘空间。
(三)logging 的基本使用步骤
- 导入 logging 模块:import logging
- 配置日志:可以使用 basicConfig 进行基本配置,也可以通过创建处理器、格式化器等进行更复杂的配置。
- 基本配置示例:
logging.basicConfig(
level=logging.INFO, # 设置日志级别
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # 设置日志格式
filename='app.log', # 日志输出到文件
filemode='w' # 文件模式,'w'表示覆盖写入,'a'表示追加写入
)
- 使用不同级别的日志函数:logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()
四、print vs logging
为了让大家更清楚地看到两者的区别,我们来做一个详细的对比表格:
对比项目 | logging | |
功能 | 简单的输出到控制台 | 支持多种日志级别,可输出到控制台和文件,支持自定义格式 |
学习成本 | 低,无需导入模块,直接使用 | 稍高,需要了解日志级别、配置等知识 |
输出控制 | 无法控制输出级别,只能输出到控制台 | 可灵活控制日志级别,可输出到多个目标(控制台、文件等) |
可读性 | 输出内容单一,无额外信息,容易混乱 | 可包含时间、日志级别、模块名等信息,结构清晰 |
适用场景 | 简单的临时调试,快速查看变量值 | 复杂项目开发、生产环境监控、需要记录日志以便后续分析 |
调试后处理 | 需要手动删除或注释掉代码中的 print 语句 | 无需删除,可通过配置灵活控制日志的输出 |
从表格中可以明显看出,print 就像一个 “临时工”,在简单的调试场景中能快速帮我们解决问题,但在复杂的项目中就显得力不从心了;而 logging 则像一个 “正式工”,具备完善的功能,能够应对各种复杂的日志记录和调试需求。
五、到底该怎么选?
(一)选 print 的情况
- 简单临时调试:当你只是想快速查看某个变量的值,或者确认某段代码是否执行到了,这种简单的临时调试场景,用 print 非常方便,不用考虑太多配置,随手就来。比如写一个小脚本,处理一些简单的数据,中间想看看某个步骤的数据是否正确,直接加个 print 就行。
- 快速定位问题:在代码出现问题时,为了快速确定问题出现的位置,比如在一个条件判断里,想知道是否进入了某个分支,用 print 输出一条简单的信息,马上就能知道结果。
(二)选 logging 的情况
- 复杂项目开发:在大型项目中,代码结构复杂,模块众多,需要记录不同级别的日志来跟踪程序的运行状态。比如一个 Web 应用,需要记录用户的请求信息、数据库操作信息、错误信息等,这时候 logging 就能很好地发挥作用,让我们能够清晰地了解程序的运行情况。
- 生产环境监控:程序上线后,需要对其运行状态进行监控,这时候日志就变得非常重要。logging 可以把日志记录到文件中,我们可以通过分析日志文件,及时发现程序中的问题,比如错误信息、警告信息等,以便及时处理。
- 需要保留日志记录:如果我们需要对程序的运行过程进行回顾和分析,比如统计程序的运行时间、处理的数据量、出现的错误次数等,logging 记录到文件中的日志就可以为我们提供详细的信息。
print 和 logging 各有各的优缺点和适用场景。print 简单易用,适合简单的临时调试;logging 功能强大,适合复杂项目和生产环境。作为开发者,我们需要根据实际情况选择合适的工具。
在开发的初期阶段,当我们还在调试代码、梳理逻辑的时候,可以适当使用 print 来快速查看信息;而当项目逐渐复杂,或者我们需要考虑程序的可维护性、可扩展性以及生产环境的需求时,logging 就是更好的选择。
最后,给大家做个对比表格,帮助理解两者的区别和适用场景:
工具 | 核心特点 | 适用场景 |
简单临时调试、快速定位问题、无需保留日志 | 简单场景(临时变量查看、快速验证逻辑) | |
logging | 复杂项目开发、生产环境监控、需要保留日志 | 复杂场景(大型项目调试、线上故障追踪、运行状态分析) |