You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下Python print() ASCII颜色码运行一段时间后失效解决方案

问题根因

Windows 控制台对ANSI转义颜色码的解析能力,依赖输出句柄上的ENABLE_VIRTUAL_TERMINAL_PROCESSING(取值0x0004)标志位:

  • 程序启动初期Python默认给控制台句柄开启了该标志,因此彩色输出可以正常工作
  • 代码中调用的Win32原生API、第三方控制台操作类库、异常输出流操作都可能意外清除该标志位。标志被清除后转义序列不会被终端解析,ESC控制符会以←明文形式打印,颜色效果完全失效
  • os.system('')之所以能临时修复,是因为拉起的cmd子进程会重新为关联控制台设置该标志,但每次调用都需要创建独立进程,高频打印场景下性能损耗极高,不适合生产环境使用。
最优修复方案(零额外依赖、一次配置永久生效)

直接在程序入口处(所有打印逻辑、第三方库初始化逻辑执行前),通过ctypes调用Windows Kernel32 API手动给stdout、stderr句柄永久开启VT处理标志,全程只需要执行1次,无任何运行时额外开销:

import ctypes
import sys

# Windows Kernel32 控制台相关常量
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12

def init_windows_console_vt():
    # 非Windows系统、输出流不是交互式终端的场景直接跳过,避免转义码写入日志文件
    if sys.platform != "win32":
        return
    if not hasattr(sys.stdout, "isatty") or not sys.stdout.isatty():
        return
    
    kernel32 = ctypes.windll.kernel32
    # 分别给标准输出、标准错误流设置VT处理标志
    for handle_id in (STD_OUTPUT_HANDLE, STD_ERROR_HANDLE):
        handle = kernel32.GetStdHandle(handle_id)
        mode = ctypes.c_ulong()
        if not kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
            continue
        # 追加VT处理标志,不修改原有控制台配置
        new_mode = mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING
        kernel32.SetConsoleMode(handle, new_mode)

# 程序启动第一时间调用即可
init_windows_console_vt()

如果代码中存在会主动修改控制台模式的第三方库,只需要在这类库的初始化调用完成后再执行一次init_windows_console_vt()即可,不需要在每次打印前重复调用。

可选优化:兼容非TTY输出场景

可以给自定义的printc函数增加TTY判断逻辑,当输出目标不是交互式终端(比如重定向到文件、管道)时自动跳过转义码拼接,避免日志文件中出现无意义的控制字符,同时完全兼容内置print()的参数逻辑:

# 颜色映射表
COLOR_MAP = {
    "default": "0",
    "black": "30",
    "red": "31",
    "green": "32",
    "yellow": "33",
    "blue": "34",
    "magenta": "35",
    "cyan": "36",
    "white": "37"
}

def printc(*values, sep=" ", end="\n", file=None, flush=False, color="default", bright=False):
    file = file if file is not None else sys.stdout
    # 非终端场景直接输出原始文本,不拼接转义码
    if not (hasattr(file, "isatty") and file.isatty()):
        print(*values, sep=sep, end=end, file=file, flush=flush)
        return
    # 拼接颜色控制码
    color_code = COLOR_MAP.get(color, "0")
    prefix = f"\u001b[{1 if bright else 0};{color_code}m"
    suffix = "\u001b[0m"
    print(prefix + sep.join(map(str, values)) + suffix, sep="", end=end, file=file, flush=flush)
其他可选方案

如果不想直接调用ctypes,可以使用colorama库,它的初始化逻辑本质和上述ctypes实现一致,会自动维护控制台VT标志位,只需要在程序启动时执行一次初始化调用即可,性能远高于反复调用os.system('')。

注意:不要在每次打印时重复执行控制台初始化逻辑,无论是ctypes实现还是第三方库初始化,一次执行即可永久生效,重复调用无意义还会带来不必要的开销。

内容的提问来源于stack exchange,提问作者K.Mulier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:54:17