Временно перенаправить stdout/stderr
можно ли временно перенаправить stdout/stderr в Python (т. е. на время действия метода)?
Edit:
проблема с текущими решениями (которые я сначала вспомнил, но потом забыл) заключается в том, что они не редирект; скорее, они просто заменяют потоки в полном объеме. Следовательно, если метод имеет local скопировать одной переменной по какой-либо причине (например, потому что поток был передан в качестве параметра что-то), это не сработает.
какие решения?
9 ответов:
чтобы решить проблему, что некоторые функции могли бы кэшировать
sys.stdout
поток как локальная переменная и, следовательно, замена глобальногоsys.stdout
не будет работать внутри этой функции, вы можете перенаправить на уровне дескриптора файла (sys.stdout.fileno()
) например:from __future__ import print_function import os import sys def some_function_with_cached_sys_stdout(stdout=sys.stdout): print('cached stdout', file=stdout) with stdout_redirected(to=os.devnull), merged_stderr_stdout(): print('stdout goes to devnull') some_function_with_cached_sys_stdout() print('stderr also goes to stdout that goes to devnull', file=sys.stderr) print('stdout is back') some_function_with_cached_sys_stdout() print('stderr is back', file=sys.stderr)
stdout_redirected()
перенаправляет все выходные данные дляsys.stdout.fileno()
к заданному имени файла, файловому объекту или файловому дескриптору (os.devnull
в Примере).
stdout_redirected()
иmerged_stderr_stdout()
определены здесь.
вы также можете поместить логику перенаправления в contextmanager.
import os import sys class RedirectStdStreams(object): def __init__(self, stdout=None, stderr=None): self._stdout = stdout or sys.stdout self._stderr = stderr or sys.stderr def __enter__(self): self.old_stdout, self.old_stderr = sys.stdout, sys.stderr self.old_stdout.flush(); self.old_stderr.flush() sys.stdout, sys.stderr = self._stdout, self._stderr def __exit__(self, exc_type, exc_value, traceback): self._stdout.flush(); self._stderr.flush() sys.stdout = self.old_stdout sys.stderr = self.old_stderr if __name__ == '__main__': devnull = open(os.devnull, 'w') print('Fubar') with RedirectStdStreams(stdout=devnull, stderr=devnull): print("You'll never see me") print("I'm back!")
Я не уверен, что означает временное перенаправление. Но вы можете переназначить потоки, как это и сбросить его обратно.
temp = sys.stdout sys.stdout = sys.stderr sys.stderr = temp
и записи в sys.stderr в пределах печати stmts, как это.
print >> sys.stderr, "Error in atexit._run_exitfuncs:"
обычная печать будет в stdout.
это возможно с декоратором, например:
import sys def redirect_stderr_stdout(stderr=sys.stderr, stdout=sys.stdout): def wrap(f): def newf(*args, **kwargs): old_stderr, old_stdout = sys.stderr, sys.stdout sys.stderr = stderr sys.stdout = stdout try: return f(*args, **kwargs) finally: sys.stderr, sys.stdout = old_stderr, old_stdout return newf return wrap
как использовать:
@redirect_stderr_stdout(some_logging_stream, the_console): def fun(...): # whatever
или, если вы не хотите изменить источник
fun
, назовите его непосредственно какredirect_stderr_stdout(some_logging_stream, the_console)(fun)
но обратите внимание, что это не является потокобезопасным.
начиная с python 3.4 есть контекстный менеджер
contextlib.redirect_stdout
:from contextlib import redirect_stdout with open('yourfile.txt', 'w') as f: with redirect_stdout(f): # do stuff...
в полной тишине
stdout
это работает:from contextlib import redirect_stdout with redirect_stdout(None): # do stuff...
вот контекстный менеджер, который я нашел полезным. Хорошие вещи об этом, что вы можете использовать его с
with
оператор и он также обрабатывает перенаправление для дочерних процессов.import contextlib @contextlib.contextmanager def stdchannel_redirected(stdchannel, dest_filename): """ A context manager to temporarily redirect stdout or stderr e.g.: with stdchannel_redirected(sys.stderr, os.devnull): ... """ try: oldstdchannel = os.dup(stdchannel.fileno()) dest_file = open(dest_filename, 'w') os.dup2(dest_file.fileno(), stdchannel.fileno()) yield finally: if oldstdchannel is not None: os.dup2(oldstdchannel, stdchannel.fileno()) if dest_file is not None: dest_file.close()
контекст для того, почему я создал это в этот блог.
Компания Raymond Hettinger показывает нам лучший способ[1]:
import sys with open(filepath + filename, "w") as f: #replace filepath & filename with f as sys.stdout: print("print this to file") #will be written to filename & -path
после блока с блоком sys.стандартный вывод будет сброшен
[1]: http://www.youtube.com/watch?v=OSGv2VnC0go&list=PLQZM27HgcgT-6D0w6arhnGdSHDcSmQ8r3
мы будем использовать
PHP
синтаксис ob_start и ob_get_contents функции в python3, и перенаправить входные данные в файл.выходные данные хранятся в файле, также может использоваться любой тип потока.
from functools import partial output_buffer = None print_orig = print def ob_start(fname="print.txt"): global print global output_buffer print = partial(print_orig, file=output_buffer) output_buffer = open(fname, 'w') def ob_end(): global output_buffer close(output_buffer) print = print_orig def ob_get_contents(fname="print.txt"): return open(fname, 'r').read()
использование:
print ("Hi John") ob_start() print ("Hi John") ob_end() print (ob_get_contents().replace("Hi", "Bye"))
печати
Привет, Джон Пока Джон
посмотреть contextlib.redirect_stdout (new_target) и contextlib.redirect_stderr (new_target). contextlib.redirect_stderr (new_target) нового в Python 3.5.