1. 배경
파이썬에서 파일 객체 sys.stdin
, sys.stdout
, sys.stderr
는 표준 입력, 표준 출력, 표준에 해당합니다. 통역사 각각 오류 흐름. 프로그램이 시작되면 이러한 개체의 초기 값은 sys.__stdin__
, sys.__stdout__
및 sys.__stderr__
에 의해 저장되므로 종료 중에 표준 스트림 개체를 복원하는 데 사용할 수 있습니다.
Windows 시스템의 IDLE(Python GUI)은 pythonw.exe에 의해 실행되며 GUI에는 콘솔이 없습니다. 따라서 IDLE은 표준 출력 핸들을 특수 PseudoOutputFile 개체로 대체하여 스크립트 출력이 IDLE 터미널 창(셸)으로 리디렉션되도록 합니다. 이로 인해 다음과 같은 이상한 문제가 발생할 수 있습니다.
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 <idlelib.PyShell.PseudoInputFile object at 0x0177C910> <idlelib.PyShell.PseudoOutputFile object at 0x0177C970> <idlelib.PyShell.PseudoOutputFile object at 0x017852B0> >>> for fd in (sys.__stdin__, sys.__stdout__, sys.__stderr__): print fd <open file '<stdin>', mode 'r' at 0x00FED020> <open file '<stdout>', mode 'w' at 0x00FED078> <open file '<stderr>', mode 'w' at 0x00FED0D0> >>>
sys.__stdout__
및 sys.stdout
의 값이 같은. 일반적인 Python 인터프리터(예: Windows 콘솔을 통해)에서 위 코드를 실행할 때 두 값은 동일합니다.
print 문이 쉼표로 끝나지 않으면 개행 문자(줄 바꿈)가 출력 문자열 끝에 자동으로 추가됩니다. 그렇지 않으면 추가 개행 문자를 대체하는 데 공백이 사용됩니다. 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
2. 리디렉션 방법
이 섹션에서는 일반적으로 사용되는 Python 표준을 소개합니다. 출력 리디렉션 방법. 이러한 각 방법에는 고유한 장점과 단점이 있으며 다양한 시나리오에 적합합니다.
2.1 콘솔 리디렉션
출력을 리디렉션하는 가장 간단하고 일반적인 방법은 콘솔 명령을 사용하는 것입니다. 이 리디렉션은 콘솔에 의해 수행되며 Python 자체와는 아무 관련이 없습니다.
Windows 명령 프롬프트(cmd.exe)와 Linux 셸(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 $SHELL
또는 echo </p>
는 현재 사용되는 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 >>Redirection
print obj >> expr
이 방법은 print 문의 확장 형식, 즉 "obj
"을 기반으로 합니다. 그 중 expr
은 파일류(특히 쓰기 메소드를 제공하는 객체) 객체로, None일 경우 표준 출력(sys.stdout)에 해당한다.
memo = cStringIO.StringIO(); serr = sys.stderr; file = open('out.txt', 'w+') print >>memo, 'StringIO'; print >>serr, 'stderr'; print >>file, 'file' print >>None, memo.getvalue()
2.3 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!'
sys.stdout
IDLE에서 sys.__stdout__
의 초기 값은 PseudoOutputFile 개체입니다. sys.stdout
과는 다릅니다. 일반성을 위해 이 예제에서는 from sys import stdout
을 저장하기 위한 별도의 변수(savedStdout)를 정의합니다. 이 작업도 아래에서 수행됩니다. 또한 이 예제는
write()
다음은 다양한 요구 사항을 충족하기 위해
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 '<Silence>' #无屏显或写入 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 <raise> 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 중국어 웹사이트를 주목하세요!