1. 项目概述为什么要在终端里玩“变色龙”如果你刚开始学Python或者已经写了成百上千行代码可能大部分时间都在和黑底白字的终端或者叫控制台、命令行打交道。默认的print函数确实朴实无华输出的信息都一个样。但你想过没有当程序运行出错时如果错误信息能自动变成醒目的红色或者程序执行成功时关键结果能用绿色高亮显示又或者你想做一个命令行小游戏不同状态的文字用不同颜色区分——这不仅能极大提升代码输出的可读性更能让你的程序显得专业而有趣。这个需求的核心就是让Python的print函数输出带颜色的字体。这听起来像是个“花里胡哨”的功能但在实际开发中尤其是在开发命令行工具CLI、日志系统、自动化测试脚本甚至是教学演示时都是一个非常实用且能提升用户体验的技巧。它背后的技术并不复杂主要依赖于一个古老但广泛支持的ANSI转义序列标准。简单来说ANSI转义序列是一套特殊的字符组合当终端程序如Windows的CMD/PowerShell、macOS/Linux的Terminal识别到这些字符时不会把它们当作普通文本打印出来而是会执行一系列控制命令比如移动光标、改变文字颜色、背景色甚至加粗、闪烁等。所以我们今天要做的就是学会如何用Python的print函数巧妙地“嵌入”这些ANSI转义码从而让终端里的文字变得五彩斑斓。无论你是想美化自己的小工具还是想深入理解终端的工作原理这都是一项值得掌握的技能。2. 核心原理ANSI转义码是如何工作的在深入代码之前我们必须先搞懂背后的原理。否则你可能会遇到“在A终端能变色在B终端却显示乱码”的尴尬情况。2.1 什么是ANSI转义序列ANSI转义序列ANSI Escape Sequences起源于早期的VT100等终端是一套用于控制终端文本显示格式的标准。它以一个特殊的转义字符Escape Character开头通常是\033八进制或\x1b十六进制后面跟着一个左方括号[再后面是具体的指令代码和参数最后以一个字母通常是m结尾表示这是一个设置图形模式即颜色、样式的指令。一个最基础的格式是这样的\033[XXXm。这里的XXX就是控制代码。2.2 颜色和样式的代码表理解下面的代码表是自由调配颜色的关键。这些代码可以组合使用用分号;隔开。常用文本颜色代码前景色30: 黑色 (Black)31: 红色 (Red)32: 绿色 (Green)33: 黄色 (Yellow)34: 蓝色 (Blue)35: 品红色 (Magenta)36: 青色 (Cyan)37: 白色 (White)90: 亮黑色 (Bright Black)91: 亮红色 (Bright Red)92: 亮绿色 (Bright Green)93: 亮黄色 (Bright Yellow)94: 亮蓝色 (Bright Blue)95: 亮品红色 (Bright Magenta)96: 亮青色 (Bright Cyan)97: 亮白色 (Bright White)常用背景颜色代码40: 黑色背景41: 红色背景42: 绿色背景43: 黄色背景44: 蓝色背景45: 品红色背景46: 青色背景47: 白色背景同样也有100到107的亮色背景系列。常用文本样式代码0: 重置所有属性恢复默认1: 加粗/高亮注意在某些终端中1可能表现为更亮的颜色而非字体加粗2: 模糊/暗淡支持较少3: 斜体支持较少4: 下划线5: 闪烁支持较少7: 反显前景色和背景色互换8: 隐藏文字不可见9: 删除线支持较少注意样式和颜色的支持程度高度依赖于你使用的终端模拟器。比如Windows古老的CMD.exe对ANSI的支持非常差Win10之后有所改善但默认可能未开启而Windows Terminal、PowerShell、macOS的Terminal、Linux的GNOME Terminal或Konsole等现代终端则支持良好。斜体、闪烁等样式在很多终端中可能无法正常显示。2.3 一个简单的例子拆解我们来看一句代码print(“\033[31;1;4m重要错误\033[0m”)我们来拆解它\033[: 转义序列开始。31: 设置文本颜色为红色。;1: 设置样式为加粗/高亮。;4m: 设置样式为下划线并以m结束格式设置。此时终端接收到这个指令就会将后续输出的文字应用“红色、加粗、带下划线”的样式。重要错误: 这是实际要显示的文本。\033[0m: 这是至关重要的一步。0是重置代码。它告诉终端“到此为止后面的文字恢复默认样式”。如果你不加这个重置那么从重要错误之后这个终端会话里所有后续的输出都会是红色、加粗、带下划线的直到你关闭终端或再次发送重置指令。实操心得养成“设置样式后立刻重置”的习惯。最稳妥的做法是在需要变色的文本字符串末尾加上\033[0m。你可以把它想象成在HTML里写了一个span style”color:red”标签后面必须跟一个闭合的/span。3. 从基础到封装三种实现颜色的方法理解了原理我们就可以动手实现了。我将介绍三种由浅入深的方法你可以根据项目复杂度来选择。3.1 方法一原始字符串拼接最直接这是最基础的方法直接在print的字符串里拼接转义序列。# 定义一些颜色常量方便使用 RED ‘\033[31m’ GREEN ‘\033[32m’ YELLOW ‘\033[33m’ BLUE ‘\033[34m’ MAGENTA ‘\033[35m’ CYAN ‘\033[36m’ WHITE ‘\033[37m’ RESET ‘\033[0m’ # 重置样式 BOLD ‘\033[1m’ # 加粗 UNDERLINE ‘\033[4m’ # 下划线 print(f“{RED}这是一段红色文字{RESET}”) print(f“{GREEN}{BOLD}这是加粗的绿色文字{RESET}”) print(f“{BLUE}{UNDERLINE}这是带下划线的蓝色文字{RESET}”) print(f“{YELLOW}背景是{CYAN}青色{RESET}{YELLOW}的文字{RESET}”) # 注意嵌套和重置的位置优点简单直观无需额外依赖。适合在小型脚本或快速测试中使用。缺点代码中散布着难以阅读的转义字符影响可读性。需要手动管理RESET容易忘记。不支持Windows CMD等不兼容ANSI的终端会显示乱码。3.2 方法二使用第三方库colorama跨平台推荐这是最推荐的方法尤其当你需要确保程序在Windows、macOS、Linux上都能正常显示颜色时。colorama库的主要工作就是自动处理不同平台间的兼容性问题。在Windows上它会自动调用系统API来模拟ANSI转义序列的效果。第一步安装coloramapip install colorama第二步基本使用colorama的核心是init()函数和Fore前景色、Back背景色、Style样式这几个类。import colorama from colorama import Fore, Back, Style # 初始化colorama。autoresetTrue是神器意味着每次print后自动重置样式无需手动加RESET。 colorama.init(autoresetTrue) print(Fore.RED ‘这是红色文字’) print(Fore.GREEN Back.YELLOW ‘这是绿字黄底’) print(Fore.BLUE Style.BRIGHT ‘这是亮蓝色文字’) # BRIGHT 即加粗/高亮 print(Style.DIM ‘这是暗淡的文字’) # 注意DIM在部分终端可能不生效 # 因为设置了autoresetTrue所以这里输出是默认颜色 print(‘这行文字是默认颜色’) # 如果你想手动控制可以不设置autoreset然后使用Style.RESET_ALL colorama.init(autoresetFalse) print(Fore.MAGENTA ‘品红色文字不会自动重置’) print(Style.RESET_ALL ‘现在重置了’) # 程序结束前可以调用deinit()但通常不是必须的 # colorama.deinit()第三步使用colorama的便捷函数colorama还提供了ansi模块可以让你直接用代码生成序列或者用Cursor和AnsiCodes进行更底层的控制但对于大多数着色需求上面的用法已经足够。重要提示在编写需要跨平台的命令行工具时务必在程序入口处调用colorama.init()。这是保证Windows下颜色正常显示的关键。autoresetTrue参数能帮你避免很多因忘记重置样式导致的显示错乱问题强烈建议开启。3.3 方法三创建自己的颜色打印函数进阶封装如果你觉得每次都要写Fore.RED ‘text’还是有点啰嗦或者你想统一管理项目的日志输出格式比如错误用红色警告用黄色信息用绿色那么封装自己的打印函数是更好的选择。import sys import colorama from colorama import Fore, Style colorama.init(autoresetTrue) # 基础初始化 def print_color(text, colorFore.WHITE, styleStyle.NORMAL, end‘\n’, filesys.stdout): “”” 一个支持颜色和样式的打印函数。 :param text: 要打印的文本 :param color: 颜色来自colorama.Fore :param style: 样式来自colorama.Style :param end: 行尾字符默认为换行 :param file: 输出流默认为标准输出 “”” # 将颜色、样式和文本组合由于autoresetTrue我们不需要在字符串末尾加RESET # 但为了函数自包含我们显式组合。注意如果autoresetTruecolorama会在print后自动重置 # 但为了函数逻辑清晰这里我们手动组合给传入的file。 # 更稳妥的做法是利用colorama已经初始化的事实直接使用其提供的拼接功能。 formatted_text f“{style}{color}{text}{Style.RESET_ALL}” print(formatted_text, endend, filefile) def log_info(message): print_color(f“[INFO] {message}”, colorFore.GREEN) def log_warning(message): print_color(f“[WARN] {message}”, colorFore.YELLOW, styleStyle.BRIGHT) def log_error(message): print_color(f“[ERROR] {message}”, colorFore.RED, styleStyle.BRIGHT) def log_debug(message): print_color(f“[DEBUG] {message}”, colorFore.CYAN) # 使用示例 log_info(“程序启动成功。”) log_warning(“配置文件缺失使用默认配置。”) log_error(“无法连接到数据库”) log_debug(“当前用户ID: 12345”)封装的好处一致性整个项目的彩色输出有统一的风格。便捷性调用log_error(“xxx”)比写一长串拼接要方便得多。可维护性如果想修改错误信息的颜色比如从红色改为品红色只需要在一个地方修改log_error函数即可。扩展性可以轻松地为这个函数增加时间戳、记录到文件等功能。4. 实战应用场景与避坑指南掌握了基本方法我们来看看在实际项目中如何应用以及会遇到哪些“坑”。4.1 场景一命令行工具CLI的状态反馈假设你在写一个文件处理工具import colorama from colorama import Fore, Style import os colorama.init(autoresetTrue) def process_file(filepath): if not os.path.exists(filepath): print(Fore.RED Style.BRIGHT f“错误文件 ‘{filepath}’ 不存在”) return False try: # 模拟处理过程 print(Fore.BLUE f“正在处理: {filepath}”) # … 处理逻辑 … print(Fore.GREEN f“成功: {filepath} 处理完成。”) return True except Exception as e: print(Fore.RED f“处理失败: {e}”) return False # 模拟调用 process_file(“/tmp/test.txt”) process_file(“/nonexistent/file.log”)这样用户一眼就能看清成功、失败和错误信息。4.2 场景二增强日志系统的可读性你可以将自定义的彩色打印函数集成到Python标准的logging模块中但这需要自定义Formatter和Handler。一个更简单的方法是在需要直接输出到控制台而非文件的日志信息中使用彩色打印而文件日志保持纯文本。这符合“日志文件应避免控制字符”的最佳实践。4.3 常见问题与排查技巧实录即使使用了colorama你可能还是会遇到一些奇怪的问题。下面是我踩过的一些坑和解决方案问题1在PyCharm或VS Code的内置终端中颜色不显示或显示异常原因大多数现代IDE的内置终端都基于成熟的终端模拟器如PyCharm用自己实现的VS Code通常用系统默认对ANSI支持很好。如果颜色不显示首先检查是否正确初始化了coloramaWindows下必须。是否在字符串末尾错误地添加了额外的RESET或格式错误。IDE的终端主题是否将颜色覆盖了比如深色主题下亮黄色可能看不清。排查先在一个最简单的脚本里测试基础颜色是否工作。如果基础颜色工作但你的不工作检查字符串拼接逻辑。问题2输出到文件时文件里出现了奇怪的字符如←[31m原因ANSI转义序列是给终端看的指令不是文本内容。当你把输出重定向到文件python script.py log.txt或在代码中直接写入文件时这些转义字符会被原样写入。解决方案在输出到文件时应该禁用颜色。可以通过判断输出目标是否是终端tty来实现。import sys from colorama import Fore def print_color_conditional(text, color): if sys.stdout.isatty(): # 判断标准输出是否连接到终端 print(color text Fore.RESET) else: print(text) # 输出到文件或管道时打印无颜色的纯文本 print_color_conditional(“这条信息只在终端显示颜色”, Fore.GREEN)colorama本身不会帮你做这个判断需要你自己处理。问题3颜色在某个特定的Linux服务器或Docker容器中不显示原因终端类型TERM环境变量设置不正确或者终端模拟器本身不支持颜色。排查在终端中执行echo $TERM常见的支持颜色的值有xterm-256color,screen-256color等。如果是dumb或unknown则很可能不支持颜色。尝试强制启用颜色。许多命令行工具如ls,grep都有--coloralways参数。对于你自己的脚本可以添加一个命令行参数如--color来强制输出颜色代码让用户决定。在Docker中确保以交互式模式运行-it参数并且TERM环境变量被正确传递。问题4组合样式时效果和预期不符原因如前所述不是所有终端都支持所有样式如闪烁\033[5m、斜体\033[3m。加粗\033[1m在某些终端上可能只是让颜色更亮。建议将颜色作为主要的信息区分手段样式加粗、下划线作为辅助。并且不要依赖那些支持度不高的样式如闪烁、斜体来传递关键信息。在发布工具前最好在目标平台如Windows Terminal, macOS Terminal, 常见的Linux终端上测试一下。问题5使用colorama后程序退出时终端颜色状态异常原因如果未使用autoresetTrue并且在程序异常退出前没有重置样式终端可能会保持最后一种颜色状态。解决方案首选使用colorama.init(autoresetTrue)。这是最简单有效的办法。备选使用Python的atexit模块注册一个清理函数确保程序退出时重置颜色。import atexit import colorama from colorama import Style colorama.init(autoresetFalse) # 不自动重置 def reset_on_exit(): print(Style.RESET_ALL, end“”) atexit.register(reset_on_exit) # … 你的代码 …5. 超越基础256色与真彩色支持如果你觉得8种基础颜色不够用现代终端大多支持256色甚至24位真彩色1600万色。这可以让你实现更细腻的色彩效果。5.1 使用256色256色模式使用格式\033[38;5;{code}m设置前景色\033[48;5;{code}m设置背景色。其中{code}是0-255之间的颜色索引。def print_256_color(text, fg_code): “””使用256色打印文本””” print(f“\033[38;5;{fg_code}m{text}\033[0m”) # 打印一些不同索引的颜色 for i in range(0, 256, 16): # 每16个颜色打印一行 line “” for j in range(16): code i j if code 256: line f“\033[38;5;{code}m{code:4d}\033[0m” print(line)你可以搜索“256 color terminal chart”找到颜色索引对应的色卡选择你需要的颜色。5.2 使用24位真彩色True Color这是最强大的模式允许你直接使用RGB值。格式为\033[38;2;{r};{g};{b}m前景色和\033[48;2;{r};{g};{b}m背景色。def print_true_color(text, r, g, b): “””使用RGB真彩色打印文本””” print(f“\033[38;2;{r};{g};{b}m{text}\033[0m”) print_true_color(“这是珊瑚红色”, 255, 127, 80) print_true_color(“这是薄荷绿色”, 62, 180, 137)重要警告256色和真彩色的支持程度比基础16色要差。尤其是在一些远程SSH会话、老旧的终端模拟器或者某些配置下可能无法显示。在重要的生产环境工具中除非你明确知道目标环境支持否则应谨慎使用最好将其作为可选的增强功能并提供一个回退到基础16色的方案。对于大多数应用场景基础的16色加上亮色已经足够清晰地区分信息了。6. 最终建议与个人体会折腾终端颜色是个有趣且实用的事情它能立刻让你写的命令行工具显得与众不同。从我个人的经验来看有几点体会想分享第一跨平台兼容性是第一位。如果你写的工具哪怕有一丝可能要在Windows上运行那么colorama几乎是必选项。不要试图自己去写os.name判断然后拼接不同的转义序列colorama社区已经帮你把坑都踩平了。初始化时加上autoresetTrue能省去无数调试的烦恼。第二颜色是辅助信息是核心。不要滥用颜色。通常用红色表示错误/危险黄色表示警告绿色表示成功/正常蓝色表示信息/提示这就足够了。过多的颜色反而会让用户眼花缭乱。可以考虑为色盲用户提供模式比如用[ERROR]这样的文字前缀配合颜色。第三始终提供“无色”选项。通过判断sys.stdout.isatty()或者添加一个像--no-color这样的命令行参数让你的用户有权选择关闭颜色。这对于将输出重定向到文件进行后续处理如grep,awk的场景至关重要因为嵌入的ANSI码会干扰文本处理。最后别忘了终端颜色只是提升CLI体验的一部分。结合清晰的帮助信息argparse库、进度条tqdm库、交互式提示input/prompt_toolkit库以及良好的错误处理才能真正打造出用户爱不释手的命令行工具。从给print穿上彩色的外衣开始一步步去探索更精彩的命令行世界吧。