013、print 不止打印:format、sep、end、file 重定向、彩色输出实战

📅 2026/6/22 20:38:29
013、print 不止打印:format、sep、end、file 重定向、彩色输出实战
013、print 不止打印format、sep、end、file 重定向、彩色输出实战从一次线上事故说起去年维护一个日志采集服务凌晨三点被报警电话吵醒——生产环境某个模块的日志文件暴涨到20GB磁盘直接打满。排查下来罪魁祸首是一行代码print(ERROR: str(user_id) str(error_code) str(timestamp))这行代码每秒执行上千次每次拼接字符串都在创建新对象更致命的是它默认输出到stdout而运维同学把stdout重定向到了日志文件。一个简单的print差点让整个服务宕机。从那以后我对print的每个参数都格外敏感。今天就把这些实战经验掰开揉碎讲清楚。format别再用加号拼字符串了上面那段代码用format改写后性能提升30%以上# 别这样写——每次拼接创建3个临时字符串对象print(ERROR: str(user_id) str(error_code))# 这样写——格式化字符串只创建一次print(ERROR: {} {} {}.format(user_id,error_code,timestamp))# 更推荐f-stringPython 3.6性能最好print(fERROR:{user_id}{error_code}{timestamp})f-string里还能直接调用函数调试时特别爽# 调试利器打印变量名和值print(f{user_id},{error_code})# 输出user_id12345, error_code404这里踩过坑f-string里的表达式不要太复杂否则可读性会崩。见过同事写f{[x*2 for x in data if x 0]}调试时自己都看不懂。sep分隔符的隐藏用法sep参数常被忽略但处理列表输出时特别好用# 默认用空格分隔print(a,b,c)# a b c# 改成逗号print(a,b,c,sep,)# a,b,c# 实战生成CSV行print(2024-01-15,ERROR,timeout,sep,)# 2024-01-15,ERROR,timeout有个小技巧用sep拼接路径时比os.path.join更直观# 别这样写print(/.join([home,user,data]))# 这样写更清晰print(home,user,data,sep/)# home/user/dataend控制换行避免日志错乱默认print会在末尾加换行符\n这在写进度条或实时日志时很要命# 实时进度条不换行foriinrange(100):print(f\r进度:{i}%,end,flushTrue)time.sleep(0.1)flushTrue这个参数我经常忘加结果进度条卡住不动排查半天才发现是缓冲区没刷新。生产环境写日志时建议始终加上flush# 日志写入强制刷新缓冲区print(f[{timestamp}]{message},filelog_file,flushTrue)file重定向把print当日志工具用这是最实用的技巧。print默认输出到sys.stdout但可以重定向到任何文件对象# 写入文件withopen(app.log,a)asf:print(f[ERROR]{message},filef)# 同时输出到文件和终端importsysprint(f[INFO]{message})# 终端print(f[INFO]{message},fileopen(app.log,a))# 文件更骚的操作重定向到标准错误输出importsys# 错误信息输出到stderr方便区分print(f[FATAL]{message},filesys.stderr)这里踩过坑重定向到文件后记得关闭文件句柄。用with语句最安全否则文件描述符会泄漏。彩色输出让日志一目了然终端彩色输出靠ANSI转义码原理很简单# 颜色代码RED\033[91mGREEN\033[92mYELLOW\033[93mRESET\033[0mprint(f{RED}[ERROR]{RESET}连接超时)print(f{GREEN}[OK]{RESET}请求成功)封装成函数更实用defcolor_print(text,colorred):colors{red:\033[91m,green:\033[92m,yellow:\033[93m,blue:\033[94m}print(f{colors.get(color,)}{text}\033[0m)color_print(危险操作,red)color_print(完成,green)注意Windows终端可能不支持ANSI码需要先执行os.system(color)启用。macOS和Linux默认支持。实战一个完整的日志工具把上面所有技巧整合起来importsysimportdatetimeclassLogger:def__init__(self,log_fileNone):self.log_filelog_filedeflog(self,level,message):timestampdatetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S)formattedf[{timestamp}] [{level}]{message}# 终端输出带颜色iflevelERROR:print(f\033[91m{formatted}\033[0m,filesys.stderr,flushTrue)eliflevelWARN:print(f\033[93m{formatted}\033[0m,flushTrue)else:print(f\033[92m{formatted}\033[0m,flushTrue)# 文件输出不带颜色ifself.log_file:withopen(self.log_file,a)asf:print(formatted,filef,flushTrue)loggerLogger(app.log)logger.log(ERROR,数据库连接失败)logger.log(INFO,服务启动成功)个人经验总结生产环境别用print打日志——用logging模块但调试阶段printfile重定向比logging灵活得多f-string是首选——性能好、可读性强Python 3.6项目无脑用flushTrue是保命符——日志丢失往往是因为缓冲区没刷新彩色输出只在开发环境用——生产环境日志要纯文本方便grep和日志收集系统sep参数能替代join——处理路径、CSV时更简洁最后说一句print看似简单但用好它能让调试效率翻倍。下次遇到诡异bug试试用printfile重定向把中间变量输出到文件比断点调试快多了。