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。
首先考慮輸出抑制,基于上下文管理器語法實現如下:
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:)" #不屏顯
評論
查看更多