0

0

python标准输出重定向方式

高洛峰

高洛峰

发布时间:2017-02-25 13:11:21

|

3231人浏览过

|

来源于php中文网

原创

一. 背景

在Python中,文件对象sys.stdinsys.stdoutsys.stderr分别对应解释器的标准输入、标准输出和标准出错流。在程序启动时,这些对象的初值由sys.__stdin__sys.__stdout__sys.__stderr__保存,以便用于收尾(finalization)时恢复标准流对象。

Windows系统中IDLE(Python GUI)由pythonw.exe,该GUI没有控制台。因此,IDLE将标准输出句柄替换为特殊的PseudoOutputFile对象,以便脚本输出重定向到IDLE终端窗口(Shell)。这可能导致一些奇怪的问题,例如:

Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec 5 2015, 20:32:19) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import sys
>>> for fd in (sys.stdin, sys.stdout, sys.stderr): print fd




>>> for fd in (sys.__stdin__, sys.__stdout__, sys.__stderr__): print fd

', mode 'r' at 0x00FED020>
', mode 'w' at 0x00FED078>
', mode 'w' at 0x00FED0D0>
>>>

可以发现,sys.__stdout__sys.stdout取值并不相同。而在普通的Python解释器下(如通过Windows控制台)运行上述代码时,两者取值相同。

立即学习Python免费学习笔记(深入)”;

print语句(statement)不以逗号结尾时,会在输出字符串尾部自动附加一个换行符(linefeed);否则将一个空格代替附加的换行符。print语句默认写入标准输出流,也可重定向至文件或其他可写对象(所有提供write方法的对象)。这样,就可以使用简洁的print语句代替笨拙的object.write('hello'+'\n')写法。

由上可知,在Python中调用print obj打印对象时,缺省情况下等效于调用sys.stdout.write(obj+'\n')

示例如下:

>>> import sys
>>> print 'Hello World'
Hello World
>>> sys.stdout.write('Hello World')
Hello World

二. 重定向方式

本节介绍常用的Python标准输出重定向方式。这些方法各有优劣之处,适用于不同的场景。

2.1 控制台重定向

最简单常用的输出重定向方式是利用控制台命令。这种重定向由控制台完成,而与Python本身无关。

Windows命令提示符(cmd.exe)和Linux Shell(bash等)均通过">"或">>"将输出重定向。其中,">"表示覆盖内容,">>"表示追加内容。类似地,"2>"可重定向标准错误。重定向到"nul"(Windows)或"/dev/null"(Linux)会抑制输出,既不屏显也不存盘。

以Windows命令提示符为例,将Python脚本输出重定向到文件(为缩短篇幅已删除命令间空行):

E:\>echo print 'hello' > test.py
E:\>test.py > out.txt
E:\>type out.txt
hello
E:\>test.py >> out.txt
E:\>type out.txt
hello
hello
E:\>test.py > nul

注意,在Windows命令提示符中执行Python脚本时,命令行无需以"python"开头,系统会根据脚本后缀自动调用Python解释器。此外,type命令可直接显示文本文件的内容,类似Linux系统的cat命令。

Linux Shell中执行Python脚本时,命令行应以"python"开头。除">"或">>"重定向外,还可使用tee命令。该命令可将内容同时输出到终端屏幕和(多个)文件中,"-a"选项表示追加写入,否则覆盖写入。示例如下(echo $SHELLecho $0显示当前所使用的Shell):

[wangxiaoyuan_@localhost ~]$ echo $SHELL
/bin/bash
[wangxiaoyuan_@localhost ~]$ python -c "print 'hello'"
hello
[wangxiaoyuan_@localhost ~]$ python -c "print 'hello'" > out.txt
[wangxiaoyuan_@localhost ~]$ cat out.txt
hello
[wangxiaoyuan_@localhost ~]$ python -c "print 'world'" >> out.txt
[wangxiaoyuan_@localhost ~]$ cat out.txt             
hello
world
[wangxiaoyuan_@localhost ~]$ python -c "print 'I am'" | tee out.txt
I am
[wangxiaoyuan_@localhost ~]$ python -c "print 'xywang'" | tee -a out.txt
xywang
[wangxiaoyuan_@localhost ~]$ cat out.txt
I am
xywang
[wangxiaoyuan_@localhost ~]$ python -c "print 'hello'" > /dev/null
[wangxiaoyuan_@localhost ~]$

若仅仅想要将脚本输出保存到文件中,也可直接借助会话窗口的日志抓取功能。

注意,控制台重定向的影响是全局性的,仅适用于比较简单的输出任务。

2.2 print >>重定向

这种方式基于print语句的扩展形式,即"print obj >> expr"。其中,obj为一个file-like(尤其是提供write方法的)对象,为None时对应标准输出(sys.stdout)。expr将被输出到该文件对象中。

示例如下:

memo = cStringIO.StringIO(); serr = sys.stderr; file = open('out.txt', 'w+')
print >>memo, 'StringIO'; print >>serr, 'stderr'; print >>file, 'file'
print >>None, memo.getvalue()

上述代码执行后,屏显为"serr"和"StringIO"(两行,注意顺序),out.txt文件内写入"file"。

可见,这种方式非常灵活和方便。缺点是不适用于输出语句较多的场景。

2.3 sys.stdout重定向

将一个可写对象(如file-like对象)赋给sys.stdout,可使随后的print语句输出至该对象。重定向结束后,应将sys.stdout恢复最初的缺省值,即标准输出。

简单示例如下:

import sys
savedStdout = sys.stdout #保存标准输出流
with open('out.txt', 'w+') as file:
  sys.stdout = file #标准输出重定向至文件
  print 'This message is for file!'

sys.stdout = savedStdout #恢复标准输出流
print 'This message is for screen!'

注意,IDLE中sys.stdout初值为PseudoOutputFile对象,与sys.__stdout__并不相同。为求通用,本例另行定义变量(savedStdout)保存sys.stdout,下文也将作此处理。此外,本例不适用于经由from sys import stdout导入的stdout对象。

以下将自定义多种具有write()方法的file-like对象,以满足不同需求:

class RedirectStdout: #import os, sys, cStringIO
  def __init__(self):
    self.content = ''
    self.savedStdout = sys.stdout
    self.memObj, self.fileObj, self.nulObj = None, None, None

  #外部的print语句将执行本write()方法,并由当前sys.stdout输出
  def write(self, outStr):
    #self.content.append(outStr)
    self.content += outStr

  def toCons(self): #标准输出重定向至控制台
    sys.stdout = self.savedStdout #sys.__stdout__

  def toMemo(self): #标准输出重定向至内存
    self.memObj = cStringIO.StringIO()
    sys.stdout = self.memObj

  def toFile(self, file='out.txt'): #标准输出重定向至文件
    self.fileObj = open(file, 'a+', 1) #改为行缓冲
    sys.stdout = self.fileObj
  
  def toMute(self): #抑制输出
    self.nulObj = open(os.devnull, 'w')
    sys.stdout = self.nulObj
    
  def restore(self):
    self.content = ''
    if self.memObj.closed != True:
      self.memObj.close()
    if self.fileObj.closed != True:
      self.fileObj.close()
    if self.nulObj.closed != True:
      self.nulObj.close()
    sys.stdout = self.savedStdout #sys.__stdout__

注意,toFile()方法中,open(name[, mode[, buffering]])调用选择行缓冲(无缓冲会影响性能)。这是为了观察中间写入过程,否则只有调用close()flush()后输出才会写入文件。内部调用open()方法的缺点是不便于用户定制写文件规则,如模式(覆盖或追加)和缓冲(行或全缓冲)。

重定向效果如下:

redirObj = RedirectStdout()
sys.stdout = redirObj #本句会抑制"Let's begin!"输出
print "Let's begin!"

#屏显'Hello World!'和'I am xywang.'(两行)
redirObj.toCons(); print 'Hello World!'; print 'I am xywang.'
#写入'How are you?'和"Can't complain."(两行)
redirObj.toFile(); print 'How are you?'; print "Can't complain."
redirObj.toCons(); print "What'up?"   #屏显
redirObj.toMute(); print ''  #无屏显或写入
os.system('echo Never redirect me!')  #控制台屏显'Never redirect me!'
redirObj.toMemo(); print 'What a pity!' #无屏显或写入
redirObj.toCons(); print 'Hello?'    #屏显
redirObj.toFile(); print "Oh, xywang can't hear me" #该串写入文件
redirObj.restore()

print 'Pop up' #屏显

可见,执行toXXXX()语句后,标准输出流将被重定向到XXXX。此外,toMute()toMemo()的效果类似,均可抑制输出。

使用某对象替换sys.stdout时,尽量确保该对象接近文件对象,尤其是涉及第三方库时(该库可能使用sys.stdout的其他方法)。此外,本节替换sys.stdout的代码实现并不影响由os.popen()、os.system()os.exec*()系列方法所创建进程的标准I/O流。

2.4 上下文管理器(Context Manager)

本节严格意义上并非新的重定向方式,而是利用Pyhton上下文管理器优化上节的代码实现。借助于上下文管理器语法,可不必向重定向使用者暴露sys.stdout

首先考虑输出抑制,基于上下文管理器语法实现如下:

Python v2.4 中文手册 chm
Python v2.4 中文手册 chm

Python v2.4版chm格式的中文手册,内容丰富全面,不但是一本手册,你完全可以把她作为一本Python的入门教程,教你如何使用Python解释器、流程控制、数据结构、模板、输入和输出、错误和异常、类和标准库详解等方面的知识技巧。同时后附的手册可以方便你的查询。

下载

import sys, cStringIO, contextlib
class DummyFile:
  def write(self, outStr): pass

@contextlib.contextmanager
def MuteStdout():
  savedStdout = sys.stdout
  sys.stdout = cStringIO.StringIO() #DummyFile()
  try:
    yield
  except Exception: #捕获到错误时,屏显被抑制的输出(该处理并非必需)
    content, sys.stdout = sys.stdout, savedStdout
    print content.getvalue()#; raise
  #finally:
  sys.stdout = savedStdout

使用示例如下:

with MuteStdout():
  print "I'll show up when  is executed!" #不屏显不写入
  raise #屏显上句
  print "I'm hiding myself somewhere:)" #不屏显

再考虑更通用的输出重定向:

import os, sys
from contextlib import contextmanager
@contextmanager
def RedirectStdout(newStdout):
  savedStdout, sys.stdout = sys.stdout, newStdout
  try:
    yield
  finally:
    sys.stdout = savedStdout

使用示例如下:

def Greeting(): print 'Hello, boss!'

with open('out.txt', "w+") as file:
  print "I'm writing to you..."  #屏显
  with RedirectStdout(file):
    print 'I hope this letter finds you well!' #写入文件
  print 'Check your mailbox.'   #屏显

with open(os.devnull, "w+") as file, RedirectStdout(file):
  Greeting()           #不屏显不写入
  print 'I deserve a pay raise:)' #不屏显不写入
print 'Did you hear what I said?'  #屏显

可见,with内嵌块里的函数和print语句输出均被重定向。注意,上述示例不是线程安全的,主要适用于单线程。

当函数被频繁调用时,建议使用装饰器包装该函数。这样,仅需修改该函数定义,而无需在每次调用该函数时使用with语句包裹。示例如下:

import sys, cStringIO, functools
def MuteStdout(retCache=False):
  def decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
      savedStdout = sys.stdout
      sys.stdout = cStringIO.StringIO()
      try:
        ret = func(*args, **kwargs)
        if retCache == True:
          ret = sys.stdout.getvalue().strip()
      finally:
        sys.stdout = savedStdout
      return ret
    return wrapper
  return decorator

若装饰器MuteStdout的参数retCache为真,外部调用func()函数时将返回该函数内部print输出的内容(可供屏显);若retCache为假,外部调用func()函数时将返回该函数的返回值(抑制输出)。

MuteStdout装饰器使用示例如下:

@MuteStdout(True)
def Exclaim(): print 'I am proud of myself!'

@MuteStdout()
def Mumble(): print 'I lack confidence...'; return 'sad'

print Exclaim(), Exclaim.__name__ #屏显'I am proud of myself! Exclaim'
print Mumble(), Mumble.__name__  #屏显'sad Mumble'

在所有线程中,被装饰函数执行期间,sys.stdout都会被MuteStdout装饰器劫持。而且,函数一经装饰便无法移除装饰。因此,使用该装饰器时应慎重考虑场景。

接着,考虑创建RedirectStdout装饰器:

def RedirectStdout(newStdout=sys.stdout):
  def decorator(func):
    def wrapper(*args,**kwargs):
      savedStdout, sys.stdout = sys.stdout, newStdout
      try:
        return func(*args, **kwargs)
      finally:
        sys.stdout = savedStdout
    return wrapper
  return decorator

使用示例如下:

file = open('out.txt', "w+")
@RedirectStdout(file)
def FunNoArg(): print 'No argument.'

@RedirectStdout(file)
def FunOneArg(a): print 'One argument:', a

def FunTwoArg(a, b): print 'Two arguments: %s, %s' %(a,b)

FunNoArg()   #写文件'No argument.'
FunOneArg(1984) #写文件'One argument: 1984'
RedirectStdout()(FunTwoArg)(10,29) #屏显'Two arguments: 10, 29'
print FunNoArg.__name__      #屏显'wrapper'(应显示'FunNoArg')

file.close()

注意FunTwoArg()函数的定义和调用与其他函数的不同,这是两种等效的语法。此外,RedirectStdout装饰器的最内层函数wrapper()未使用"functools.wraps(func)"修饰,会丢失被装饰函数原有的特殊属性(如函数名、文档字符串等)。

2.5 logging模块重定向

对于代码量较大的工程,建议使用logging模块进行输出。该模块是线程安全的,可将日志信息输出到控制台、写入文件、使用TCP/UDP协议发送到网络等等。

默认情况下logging模块将日志输出到控制台(标准出错),且只显示大于或等于设置的日志级别的日志。日志级别由高到低为CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,默认级别为WARNING。

以下示例将日志信息分别输出到控制台和写入文件:

import logging
logging.basicConfig(level = logging.DEBUG,
  format = '%(asctime)s [%(levelname)s] at %(filename)s,%(lineno)d: %(message)s',
  datefmt = '%Y-%m-%d(%a)%H:%M:%S',
  filename = 'out.txt',
  filemode = 'w')

 
#将大于或等于INFO级别的日志信息输出到StreamHandler(默认为标准错误)
console = logging.StreamHandler()
console.setLevel(logging.INFO) 
formatter = logging.Formatter('[%(levelname)-8s] %(message)s') #屏显实时查看,无需时间
console.setFormatter(formatter)
logging.getLogger().addHandler(console)

logging.debug('gubed'); logging.info('ofni'); logging.critical('lacitirc')

通过对多个handler设置不同的level参数,可将不同的日志内容输入到不同的地方。本例使用在logging模块内置的StreamHandler(和FileHandler),运行后屏幕上显示:

[INFO  ] ofni
[CRITICAL] lacitirc

out.txt文件内容则为:

2016-05-13(Fri)17:10:53 [DEBUG] at test.py,25: gubed
2016-05-13(Fri)17:10:53 [INFO] at test.py,25: ofni
2016-05-13(Fri)17:10:53 [CRITICAL] at test.py,25: lacitirc

除直接在程序中设置Logger、Handler、Formatter等外,还可将这些信息写入配置文件。示例如下:

#logger.conf
###############Logger###############
[loggers]
keys=root,Logger2F,Logger2CF

[logger_root]
level=DEBUG
handlers=hWholeConsole

[logger_Logger2F]
handlers=hWholeFile
qualname=Logger2F
propagate=0

[logger_Logger2CF]
handlers=hPartialConsole,hPartialFile
qualname=Logger2CF
propagate=0

###############Handler###############
[handlers]
keys=hWholeConsole,hPartialConsole,hWholeFile,hPartialFile

[handler_hWholeConsole]
class=StreamHandler
level=DEBUG
formatter=simpFormatter
args=(sys.stdout,)

[handler_hPartialConsole]
class=StreamHandler
level=INFO
formatter=simpFormatter
args=(sys.stderr,)

[handler_hWholeFile]
class=FileHandler
level=DEBUG
formatter=timeFormatter
args=('out.txt', 'a')

[handler_hPartialFile]
class=FileHandler
level=WARNING
formatter=timeFormatter
args=('out.txt', 'w')

###############Formatter###############
[formatters]
keys=simpFormatter,timeFormatter

[formatter_simpFormatter]
format=[%(levelname)s] at %(filename)s,%(lineno)d: %(message)s

[formatter_timeFormatter]
format=%(asctime)s [%(levelname)s] at %(filename)s,%(lineno)d: %(message)s
datefmt=%Y-%m-%d(%a)%H:%M:%S

此处共创建三个Logger:root,将所有日志输出至控制台;Logger2F,将所有日志写入文件;Logger2CF,将级别大于或等于INFO的日志输出至控制台,将级别大于或等于WARNING的日志写入文件。

程序以如下方式解析配置文件和重定向输出:

import logging, logging.config

logging.config.fileConfig("logger.conf")

logger = logging.getLogger("Logger2CF")
logger.debug('gubed'); logger.info('ofni'); logger.warn('nraw')
logger.error('rorre'); logger.critical('lacitirc')

logger1 = logging.getLogger("Logger2F")
logger1.debug('GUBED'); logger1.critical('LACITIRC')

logger2 = logging.getLogger()
logger2.debug('gUbEd'); logger2.critical('lAcItIrC')

运行后屏幕上显示:

[INFO] at test.py,7: ofni
[WARNING] at test.py,7: nraw
[ERROR] at test.py,8: rorre
[CRITICAL] at test.py,8: lacitirc
[DEBUG] at test.py,14: gUbEd
[CRITICAL] at test.py,14: lAcItIrC

out.txt文件内容则为:

2016-05-13(Fri)20:31:21 [WARNING] at test.py,7: nraw
2016-05-13(Fri)20:31:21 [ERROR] at test.py,8: rorre
2016-05-13(Fri)20:31:21 [CRITICAL] at test.py,8: lacitirc
2016-05-13(Fri)20:31:21 [DEBUG] at test.py,11: GUBED
2016-05-13(Fri)20:31:21 [CRITICAL] at test.py,11: LACITIRC

三. 总结

以上就是关于Python标准输出的重定向方式的全部内容,希望对学习python的朋友们能有所帮助,如果有疑问欢迎大家留言讨论。

更多python标准输出重定向方式相关文章请关注PHP中文网!

相关文章

python速学教程(入门到精通)
python速学教程(入门到精通)

python怎么学习?python怎么入门?python在哪学?python怎么学才快?不用担心,这里为大家提供了python速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

2026.01.15

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

44

2026.01.15

公务员调剂条件 2026调剂公告时间
公务员调剂条件 2026调剂公告时间

(一)符合拟调剂职位所要求的资格条件。 (二)公共科目笔试成绩同时达到拟调剂职位和原报考职位的合格分数线,且考试类别相同。 拟调剂职位设置了专业科目笔试条件的,专业科目笔试成绩还须同时达到合格分数线,且考试类别相同。 (三)未进入原报考职位面试人员名单。

58

2026.01.15

国考成绩查询入口 国考分数公布时间2026
国考成绩查询入口 国考分数公布时间2026

笔试成绩查询入口已开通,考生可登录国家公务员局中央机关及其直属机构2026年度考试录用公务员专题网站http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/examResult/written_result.html,查询笔试成绩和合格分数线,点击“笔试成绩查询”按钮,凭借身份证及准考证进行查询。

11

2026.01.15

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

65

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

36

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

75

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

21

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

35

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP基础入门课程
PHP基础入门课程

共33课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号