Перенаправление stdout в «ничего» в Python

130

У меня большой проект, состоящий из достаточно большого количества модулей, каждый выводит что-то на стандартный вывод. Сейчас, когда проект разросся, крупных нет. из printзаявлений печати много на станд отказа , который сделал программу значительно медленнее.

Итак, теперь я хочу решить во время выполнения , печатать ли что-либо в stdout. Я не могу вносить изменения в модули, так как их много. (Я знаю, что могу перенаправить stdout в файл, но даже это значительно медленнее.)

Итак, мой вопрос: как мне перенаправить stdout ни на что, т.е. как заставить printоператор ничего не делать?

# I want to do something like this.
sys.stdout = None         # this obviously will give an error as Nonetype object does not have any write method.

В настоящее время единственная идея, которая у меня есть, - это создать класс, который имеет метод записи (который ничего не делает) и перенаправить стандартный вывод на экземпляр этого класса.

class DontPrint(object):
    def write(*args): pass

dp = DontPrint()
sys.stdout = dp

Есть ли в python для этого встроенный механизм? Или есть что-то лучше этого?

Пушпак Дагаде
источник
1
Я только что обнаружил странную вещь. Превращение sys.stdout в None действительно работает в моей программе, хотя я не уверен, почему. У меня были действительно странные проблемы с перенаправлением кодировки на os.devnull, поэтому я попробовал просто использовать None, и это сработало. Может быть, я как-то делаю это в модульном тесте Django, но я не уверен.
Teekin 02

Ответы:

233

Кросс-платформенный:

import os
import sys
f = open(os.devnull, 'w')
sys.stdout = f

В Windows:

f = open('nul', 'w')
sys.stdout = f

В Linux:

f = open('/dev/null', 'w')
sys.stdout = f
Эндрю Кларк
источник
Как тогда его отменить? Есть ли способ снова включить операторы печати после того, как вы закончите?
jj172
4
Конечно, просто сохраните ссылку на оригинал sys.stdoutи верните ее после. Например, old_stdout = sys.stdoutперед любым другим кодом, затем, sys.stdout = old_stdoutкогда вы закончите.
Эндрю Кларк
1
Примечание: это не перенаправляет на уровне , т.е. дескриптор файла, он может не перенаправить вывод внешних дочерних процессов, вывод из расширений C , что печать на C стандартный вывод напрямую os.write(1, b'abc\n'). Вам нужно os.dup2()перенаправить на уровне файлового дескриптора, см.stdout_redirected()
jfs
4
Обратите внимание, что есть sys.__stdout__для отмены перенаправления. Так что вы просто делаете sys.stdout = sys.__stdout__и не нужно хранить резервную копию.
Константин Секереш 01
30

Хороший способ сделать это - создать небольшой процессор контекста, в который вы оборачиваете свои отпечатки. Затем вы просто используете withвыражение is для отключения всего вывода.

Python 2:

import os
import sys
from contextlib import contextmanager

@contextmanager
def silence_stdout():
    old_target = sys.stdout
    try:
        with open(os.devnull, "w") as new_target:
            sys.stdout = new_target
            yield new_target
    finally:
        sys.stdout = old_target

with silence_stdout():
    print("will not print")

print("this will print")

Python 3.4+:

Python 3.4 имеет встроенный процессор контекста, поэтому вы можете просто использовать contextlib следующим образом:

import contextlib

with contextlib.redirect_stdout(None):
    print("will not print")

print("this will print")

Запуск этого кода выводит только вторую строку вывода, а не первую:

$ python test.py
this will print

Это работает кросс-платформенный (Windows + Linux + Mac OSX) и чище, чем другие ответы imho.

Эмиль Стенстрём
источник
@celticminstrel, вы совершенно правы, спасибо, что поправили меня. Я обновил код, чтобы впоследствии сбросить sys.stdout.
Эмиль Стенстрём
Здесь отсутствует проверка ошибок, но зато отличная идея
Безумный физик
Добавлен код, показывающий, как это работает и в Python 3.
Эмиль Стенстрём
15

Если вы используете python 3.4 или выше, есть простое и безопасное решение с использованием стандартной библиотеки:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")
iFreilicht
источник
3
with contextlib.redirect_stdout(None)тоже работает
Крис Когдон
@ChrisCogdon Очень хороший намек, я соответствующим образом отредактировал ответ.
iFreilicht
1
Не работает, если вы используете библиотеку pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy
@Speeeddy, вы получали эту ошибку с python 2? Вы можете создать класс с фиктивным writeметодом, который ничего не делает. Смотрите мой ответ ниже.
dizcza
8

(по крайней мере, в моей системе) кажется, что запись в os.devnull примерно в 5 раз быстрее, чем запись в класс DontPrint, т.е.

#!/usr/bin/python
import os
import sys
import datetime

ITER = 10000000
def printlots(out, it, st="abcdefghijklmnopqrstuvwxyz1234567890"):
   temp = sys.stdout
   sys.stdout = out
   i = 0
   start_t = datetime.datetime.now()
   while i < it:
      print st
      i = i+1
   end_t = datetime.datetime.now()
   sys.stdout = temp
   print out, "\n   took", end_t - start_t, "for", it, "iterations"

class devnull():
   def write(*args):
      pass


printlots(open(os.devnull, 'wb'), ITER)
printlots(devnull(), ITER)

дал следующий результат:

<open file '/dev/null', mode 'wb' at 0x7f2b747044b0> 
   took 0:00:02.074853 for 10000000 iterations
<__main__.devnull instance at 0x7f2b746bae18> 
   took 0:00:09.933056 for 10000000 iterations
DonP
источник
4
Для протокола: в следующий раз, когда вы захотите что-то рассчитать
Антуан Пелисс
6

Если вы работаете в среде Unix (включая Linux), вы можете перенаправить вывод на /dev/null:

python myprogram.py > /dev/null

И для Windows:

python myprogram.py > nul
Ник Рэдфорд
источник
2
Лучше иметь решение, которое работало бы на всех платформах.
Pushpak Dagade
2
@Guanidene, возможно, но если он единственный человек, использующий его программу, он может в значительной степени гарантировать окружающую среду.
Ник Рэдфорд
nul = myfunc() ?
Hicsy
1

Ваш класс будет работать нормально (за исключением имени write()метода - его нужно вызывать в write()нижнем регистре). Просто убедитесь, что вы сохранили копиюsys.stdout в другой переменной.

Если вы используете * NIX, вы можете это сделать sys.stdout = open('/dev/null'), но это менее переносимо, чем создание собственного класса.

Рэйф Кеттлер
источник
1

Как насчет этого:

from contextlib import ExitStack, redirect_stdout
import os

with ExitStack() as stack:
    if should_hide_output():
        null_stream = open(os.devnull, "w")
        stack.enter_context(null_stream)
        stack.enter_context(redirect_stdout(null_stream))
    noisy_function()

При этом используются функции модуля contextlib, чтобы скрыть вывод любой команды, которую вы пытаетесь запустить, в зависимости от результатаshould_hide_output() , а затем восстанавливает поведение вывода после того, как эта функция будет завершена.

Если вы хотите скрыть стандартный вывод ошибок, импортируйте redirect_stderrиз contextlibи добавьте строку, говорящуюstack.enter_context(redirect_stderr(null_stream)) .

Основным недостатком является то, что это работает только в Python 3.4 и более поздних версиях.

Элиас Замария
источник
1

Вы можете просто издеваться над этим.

import mock

sys.stdout = mock.MagicMock()
Картик Раджа
источник
0

Почему бы тебе не попробовать это?

sys.stdout.close()
sys.stderr.close()
Вирендра Какуману
источник
Потому что A) это может даже не сработать, и B) если это произойдет, вы получите ошибку, а не просто подавите вывод.
Mad Physicist
0
sys.stdout = None

Это нормально для print()случая. Но это может вызвать ошибку, если вы вызовете какой-либо метод sys.stdout, например sys.stdout.write().

В документах есть примечание :

При некоторых условиях stdin, stdout и stderr, а также исходные значения stdin , stdout и stderr могут иметь значение None. Обычно это касается приложений Windows GUI, которые не подключены к консоли, и приложений Python, запускаемых с помощью pythonw.

Александр Чжень
источник
«ОК» и «Не выдаст ошибку при попытке печати» в этом случае - разные вещи.
Mad Physicist
1
Ты прав. Вы не можете вызвать какой-либо метод, sys.stdout = Nullпоэтому в некоторых случаях это может быть опасно.
Александр Чжень
В этом случае известно, что это опасно. OP специально обращается к ошибке, которую он получает в результате этого.
Mad Physicist
0

Дополнение к ответу iFreilicht - он работает как для python 2, так и для 3.

import sys

class NonWritable:
    def write(self, *args, **kwargs):
        pass

class StdoutIgnore:
    def __enter__(self):
        self.stdout_saved = sys.stdout
        sys.stdout = NonWritable()
        return self

    def __exit__(self, *args):
        sys.stdout = self.stdout_saved

with StdoutIgnore():
    print("This won't print!")
dizcza
источник