应在无法修改原始调用逻辑但需捕获或替换标准流时重定向sys.stdin/sys.stdout,典型场景包括单元测试、CLI集成测试、日志转字符串及Jupyter中拦截input();推荐用contextlib.redirect_stdout和io.StringIO安全内存重定向。

什么时候该用 sys.stdin 和 sys.stdout 重定向
不是所有输入输出都需要重定向——只有当你无法修改原始调用逻辑,但又想捕获或替换标准流时才真正需要。典型场景包括:单元测试中验证打印内容、CLI 工具集成测试、临时把日志转到字符串而非终端、或在 Jupyter 中拦截 input() 的行为。
常见错误是直接改 print() 或写死文件路径,结果导致代码耦合、难以测试。正确做法是让业务逻辑只依赖 sys.stdin 和 sys.stdout,再由外层控制流向。
- 重定向
sys.stdin前,确保原输入已关闭或保存(否则可能引发ValueError: I/O operation on closed file) -
input()函数底层读取的是sys.stdin,所以重定向后它会自动从新源读取 - 重定向
sys.stdout后,所有未指定file=参数的print()都会写入新目标
StringIO 是最安全的内存重定向方式
相比临时写文件或修改全局 sys.stdout,用 io.StringIO 拦截输出更轻量、可回溯、无副作用。它模拟文件接口,但数据留在内存里,适合断言和调试。
注意 Python 2 和 3 的差异:Python 2 用 StringIO.StringIO,Python 3 统一为 io.StringIO;如果代码需兼容两者,建议用 try/except ImportError 分支处理。
立即学习“Python免费学习笔记(深入)”;
- 写入后必须调用
.seek(0)才能从头读取内容,否则.read()返回空字符串 -
StringIO不支持二进制模式,要处理字节流得用io.BytesIO - 用完记得
.close(),尤其在长生命周期对象中,避免资源泄漏
import io import sysold_stdout = sys.stdout sys.stdout = captured = io.StringIO() print("hello") sys.stdout = old_stdout captured.seek(0) assert captured.read() == "hello\n"
用 contextlib.redirect_stdout 避免手动恢复
手动保存/恢复 sys.stdout 容易遗漏异常路径,导致后续输出错乱。Python 3.4+ 提供的 contextlib.redirect_stdout 自动处理这些边界情况,是最推荐的实操方式。
Python v2.4版chm格式的中文手册,内容丰富全面,不但是一本手册,你完全可以把她作为一本Python的入门教程,教你如何使用Python解释器、流程控制、数据结构、模板、输入和输出、错误和异常、类和标准库详解等方面的知识技巧。同时后附的手册可以方便你的查询。
它本质是上下文管理器,进入时重定向,退出时无论是否异常都会还原。但要注意:它只影响当前线程,多线程下不跨线程生效;且不能嵌套重定向同一对象(比如两次 redirect_stdout 到同一个 StringIO 实例会出错)。
- 参数必须是类文件对象(有
write()方法),不能传路径字符串或普通字符串 - 如果被重定向的目标本身抛异常(如写入只读
StringIO),异常会在with块内抛出 - 函数内部若显式指定
print(..., file=sys.stderr),不会被该上下文捕获
from contextlib import redirect_stdout import iof = io.StringIO() with redirect_stdout(f): print("captured") print("also captured") f.seek(0) print(f.read()) # → "captured\nalso captured\n"
重定向后 input() 和 raw_input() 的行为差异
Python 3 的 input() 等价于 Python 2 的 raw_input(),都从 sys.stdin 读;而 Python 2 的 input() 会 eval() 输入内容,早已弃用。重定向 sys.stdin 后,input() 会按行读取新源,但要注意换行符处理。
常见坑是用 StringIO 模拟输入时忘了末尾换行——input() 会阻塞等待换行符,导致测试卡住。另外,input() 默认 strip 掉末尾 \n,所以 StringIO("abc\n") 被读取后得到的是 "abc",不是 "abc\n"。
- 测试多行输入时,用
StringIO("line1\nline2\n"),不是"line1\nline2"(缺最后换行) - 重定向
sys.stdin后,sys.stdin.readline()行为一致,但input()更常用也更安全 - 若需模拟用户中断(Ctrl+D),向
StringIO写入空字符串并确保其为最后一行即可触发EOFError
重定向看似简单,真正难的是清理时机和跨环境一致性——比如在 pytest 中用完没还原 sys.stdout,会影响后续测试;或者在 Windows 下用 \r\n 写入 StringIO,却在 Linux 断言时用 \n 匹配失败。这些细节比语法本身更常导致问题。









