Redirecionando stdout para "nothing" em python


128

Eu tenho um grande projeto que consiste em um número suficientemente grande de módulos, cada um imprimindo algo na saída padrão. Agora, como o projeto cresceu em tamanho, não há grandes. de printinstruções imprimindo muito no padrão, o que tornou o programa consideravelmente mais lento.

Então, agora eu quero decidir em tempo de execução se deve ou não imprimir algo no stdout. Não posso fazer alterações nos módulos, pois há muitos deles. (Eu sei que posso redirecionar o stdout para um arquivo, mas mesmo isso é consideravelmente lento.)

Portanto, minha pergunta é: como redireciono o stdout para nada, ou seja, como faço para que a printinstrução não faça nada?

# 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.

Atualmente, a única idéia que tenho é criar uma classe que tenha um método de gravação (que não faça nada) e redirecionar o stdout para uma instância dessa classe.

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

dp = DontPrint()
sys.stdout = dp

Existe um mecanismo embutido em python para isso? Ou há algo melhor que isso?



1
Acabei de descobrir uma coisa peculiar. Transformar o sys.stdout em None realmente funciona no meu programa, embora não tenha certeza do motivo. Eu estava tendo problemas realmente estranhos ao redirecionar a codificação para os.devnull, então tentei usar None e ele funciona. Pode ter algo a ver comigo em um teste de unidade do Django, mas não tenho certeza.
Teekin 02/01/19

Respostas:


233

Plataforma cruzada:

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

No Windows:

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

No Linux:

f = open('/dev/null', 'w')
sys.stdout = f

Como você o cancela? Existe uma maneira de ativar as declarações de impressão depois que você terminar?
jj172

4
Claro, salve uma referência ao original sys.stdoute coloque-a novamente depois. Por exemplo, old_stdout = sys.stdoutantes de qualquer outro código, sys.stdout = old_stdoutquando terminar.
Andrew Clark

1
Nota: ele não redireciona no nível do descritor de arquivo, ou seja, pode falhar em redirecionar a saída de processos filhos externos, a saída das extensões C que são impressas diretamente no C stdout os.write(1, b'abc\n'),. Você precisa os.dup2()redirecionar no nível do descritor de arquivo, consultestdout_redirected()
jfs 22/03

4
Observe que existe sys.__stdout__para cancelar um redirecionamento. Então você faz sys.stdout = sys.__stdout__e não precisa armazenar o backup.
218 Konstantin Sekeresh #

28

Uma boa maneira de fazer isso é criar um pequeno processador de contexto no qual você imprime suas impressões. Em seguida, basta usar está em uma withdeclaração para silenciar toda a saída.

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 ou superior:

O Python 3.4 possui um processador de contexto como este embutido, então você pode simplesmente usar o contextlib como este:

import contextlib

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

print("this will print")

A execução desse código imprime apenas a segunda linha de saída, não a primeira:

$ python test.py
this will print

Isso funciona em várias plataformas (Windows + Linux + Mac OSX) e é mais limpo do que as outras respostas.


@celticminstrel você está totalmente certo, obrigado por me corrigir. Atualizei o código para redefinir o sys.stdout posteriormente.
Emil Stenström

Isso está faltando alguma verificação de erros, mas ótima idéia
Mad Físico

Adicionado código para mostrar como isso também funciona no Python 3.
Emil Stenström

15

Se você estiver no python 3.4 ou superior, há uma solução simples e segura usando a biblioteca padrão:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")

3
with contextlib.redirect_stdout(None)funciona bem
Chris Cogdon

@ChrisCogdon Dica muito boa, editei a resposta de acordo.
iFreilicht 20/02

1
Não funciona se você estiver usando a biblioteca pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy 6/09/19

@ Speeeddy você recebeu esse erro com o python 2? Você pode criar uma classe com um writemétodo fictício que não faz nada. Veja minha resposta abaixo.
dizcza

8

(pelo menos no meu sistema) parece que escrever para os.devnull é cerca de 5x mais rápido do que escrever para uma classe DontPrint, ou seja,

#!/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)

deu a seguinte saída:

<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

4
Apenas para o registro, da próxima vez que você quiser vez que algo, é só usar timeit
Antoine Pelisse

6

Se você estiver em um ambiente Unix (Linux incluído), poderá redirecionar a saída para /dev/null:

python myprogram.py > /dev/null

E para Windows:

python myprogram.py > nul

2
É melhor ter uma solução que funcione em todas as plataformas.
Pushpak Dagade

2
@Guanidene, talvez, mas se ele é a única pessoa que usa seu programa, ele pode garantir o meio ambiente.
22811 Nick Radford

nul = myfunc() ?
Hicsy

2

Que tal agora:

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()

Isso usa os recursos do módulo contextlib para ocultar a saída de qualquer comando que você esteja tentando executar, dependendo do resultado should_hide_output()e, em seguida, restaura o comportamento da saída após a execução da função.

Se você deseja ocultar saída de erro padrão, em seguida, importar redirect_stderra partir contextlibe adicione uma linha dizendo stack.enter_context(redirect_stderr(null_stream)).

A principal desvantagem é que isso só funciona no Python 3.4 e versões posteriores.



1

Sua classe funcionará bem (com exceção do write()nome do método - ele precisa ser chamado em write()minúsculas). Apenas certifique-se de salvar uma cópia sys.stdoutem outra variável.

Se você estiver no * NIX, poderá fazê-lo sys.stdout = open('/dev/null'), mas isso é menos portátil do que rolar sua própria classe.


1

Você pode simplesmente zombar.

import mock

sys.stdout = mock.MagicMock()

0

Por que você não tenta isso?

sys.stdout.close()
sys.stderr.close()

Como A) pode até não funcionar, e B) se funcionar, você receberá um erro em vez de suprimir a saída.
May Physicsist

0
sys.stdout = None

Tudo bem para o print()caso. Mas isso pode causar um erro se você chamar qualquer método de sys.stdout, por exemplo sys.stdout.write().

Há uma observação nos documentos :

Sob algumas condições, stdin, stdout e stderr, bem como os valores originais stdin , stdout e stderr, podem ser Nenhum. Geralmente é o caso de aplicativos GUI do Windows que não estão conectados a um console e aplicativos Python iniciados com pythonw.


"OK" e "Não emitirá um erro no momento em que você tentar imprimir" são coisas muito diferentes nesse caso.
Mad Physicist

1
Você está certo. Você não pode chamar nenhum método com sys.stdout = Nullisso, em alguns casos, pode ser perigoso.
Alexander Chzhen

Neste caso exato, é conhecido por ser perigoso. O OP aborda especificamente o erro que ele recebe ao fazê-lo.
Mad Physicist

0

Complemento à resposta da iFreilicht - funciona tanto para o python 2 quanto para o 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!")
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.