Rediriger stdout vers «rien» en python

128

J'ai un grand projet composé d'un nombre suffisamment grand de modules, chacun imprimant quelque chose à la sortie standard. Maintenant que le projet a pris de l'ampleur, il n'y en a pas. des printdéclarations s'impriment beaucoup sur le std, ce qui a considérablement ralenti le programme.

Donc, je veux maintenant décider au moment de l' exécution s'il faut ou non imprimer quelque chose sur le stdout. Je ne peux pas faire de changements dans les modules car ils sont nombreux. (Je sais que je peux rediriger la sortie standard vers un fichier, mais même cela est considérablement lent.)

Ma question est donc de savoir comment rediriger la sortie stdout vers rien, c'est-à-dire comment faire en sorte que l' printinstruction ne fasse rien?

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

Actuellement, la seule idée que j'ai est de créer une classe qui a une méthode d'écriture (qui ne fait rien) et de rediriger le stdout vers une instance de cette classe.

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

dp = DontPrint()
sys.stdout = dp

Existe-t-il un mécanisme intégré en python pour cela? Ou y a-t-il quelque chose de mieux que ça?

Pushpak Dagade
la source
1
Je viens de découvrir une chose étrange. Transformer sys.stdout en None fonctionne réellement dans mon programme, même si je ne sais pas pourquoi. J'avais des problèmes vraiment étranges avec le codage en redirection vers os.devnull, alors j'ai essayé d'utiliser None, et cela fonctionne. Cela pourrait avoir quelque chose à voir avec moi le faisant dans un test unitaire Django, mais je ne suis pas sûr.
Teekin

Réponses:

233

Multiplateforme:

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

Sous Windows:

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

Sous Linux:

f = open('/dev/null', 'w')
sys.stdout = f
Andrew Clark
la source
Comment l'annuler ensuite? Existe-t-il un moyen de réactiver l'impression des relevés une fois que vous avez terminé?
jj172
4
Bien sûr, enregistrez simplement une référence à l'original sys.stdoutet réinstallez-la après. Par exemple, old_stdout = sys.stdoutavant tout autre code, puis sys.stdout = old_stdoutlorsque vous avez terminé.
Andrew Clark
1
Note: il ne redirige pas à un niveau de descripteur de fichier par exemple, il peut ne pas rediriger la sortie de processus enfants externes, la sortie des extensions C à C que l' impression stdout directement os.write(1, b'abc\n'). Vous devez os.dup2(), pour rediriger au niveau du descripteur de fichier, voirstdout_redirected()
jfs
4
Notez qu'il y a sys.__stdout__pour annuler une redirection. Donc, vous faites juste sys.stdout = sys.__stdout__et pas besoin de stocker la sauvegarde.
Konstantin Sekeresh
28

Une bonne façon de faire est de créer un petit processeur de contexte dans lequel vous enveloppez vos impressions. Il vous suffit ensuite d'utiliser est dans une withdéclaration pour faire taire toute la sortie.

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 a un processeur de contexte comme celui-ci intégré, vous pouvez donc simplement utiliser contextlib comme ceci:

import contextlib

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

print("this will print")

L'exécution de ce code imprime uniquement la deuxième ligne de sortie, pas la première:

$ python test.py
this will print

Cela fonctionne multi-plateforme (Windows + Linux + Mac OSX), et est plus propre que les autres réponses à mon humble avis.

Emil Stenström
la source
@celticminstrel vous avez tout à fait raison, merci de me corriger. J'ai mis à jour le code pour réinitialiser sys.stdout par la suite.
Emil Stenström le
Cela manque une vérification d'erreur, mais bonne idée
Mad Physicist
Ajout de code pour montrer comment cela fonctionne également dans Python 3.
Emil Stenström le
15

Si vous êtes en python 3.4 ou supérieur, il existe une solution simple et sûre utilisant la bibliothèque standard:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")
iFreilicht
la source
3
with contextlib.redirect_stdout(None)fonctionne aussi
Chris Cogdon
@ChrisCogdon Très bon indice, j'ai édité la réponse en conséquence.
iFreilicht
1
Ne fonctionne pas si vous utilisez la bibliothèque pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy
@Speeeddy avez-vous eu cette erreur avec python 2? Vous pouvez créer une classe avec une writeméthode factice qui ne fait rien. Voir ma réponse ci-dessous.
dizcza
8

(du moins sur mon système) il semble que l'écriture sur os.devnull soit environ 5 fois plus rapide que l'écriture dans une classe DontPrint, c'est-à-dire

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

a donné la sortie suivante:

<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
la source
4
Juste pour information, la prochaine fois que vous voudrez chronométrer quelque chose, utilisez simplement timeit
Antoine Pelisse
6

Si vous êtes dans un environnement Unix (Linux inclus), vous pouvez rediriger la sortie vers /dev/null:

python myprogram.py > /dev/null

Et pour Windows:

python myprogram.py > nul
Nick Radford
la source
2
Il est préférable d'avoir une solution qui fonctionnerait sur toutes les plates-formes.
Pushpak Dagade
2
@Guanidene, peut-être, mais s'il est la seule personne à utiliser son programme, il peut à peu près garantir l'environnement.
Nick Radford
nul = myfunc() ?
Hicsy
2

Que dis-tu de ça:

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

Cela utilise les fonctionnalités du module contextlib pour masquer la sortie de la commande que vous essayez d'exécuter, en fonction du résultat deshould_hide_output() , puis rétablit le comportement de sortie une fois que cette fonction est exécutée.

Si vous souhaitez masquer la sortie d'erreur standard, importez redirect_stderrdepuis contextlibet ajoutez une ligne indiquant stack.enter_context(redirect_stderr(null_stream)).

Le principal inconvénient est que cela ne fonctionne que dans Python 3.4 et les versions ultérieures.

Elias Zamaria
la source
1

Votre classe fonctionnera très bien (à l'exception du write()nom de la méthode - il doit être appelé write(), en minuscules). Assurez-vous simplement d'enregistrer une copie de sys.stdoutdans une autre variable.

Si vous êtes sur un * NIX, vous pouvez le faire sys.stdout = open('/dev/null'), mais c'est moins portable que de lancer votre propre classe.

Rafe Kettler
la source
1

Vous pouvez simplement vous en moquer.

import mock

sys.stdout = mock.MagicMock()
Karthik Raja
la source
0

Pourquoi tu n'essaies pas ça?

sys.stdout.close()
sys.stderr.close()
Veerendra Kakumanu
la source
Parce que A) cela peut même ne pas fonctionner, et B) si c'est le cas, vous obtiendrez une erreur au lieu de simplement supprimer la sortie.
Mad Physicist
0
sys.stdout = None

C'est OK pour le print()cas. Mais cela peut provoquer une erreur si vous appelez une méthode de sys.stdout, par exemple sys.stdout.write().

Il y a une note dans la documentation :

Sous certaines conditions stdin, stdout et stderr ainsi que les valeurs d'origine stdin , stdout et stderr peuvent être None. C'est généralement le cas pour les applications Windows GUI qui ne sont pas connectées à une console et les applications Python démarrées avec pythonw.

Alexander Chzhen
la source
"OK" et "Ne lancera pas d'erreur au moment où vous essayez d'imprimer" sont des choses très différentes dans ce cas.
Mad Physicist
1
Vous avez raison. Vous ne pouvez appeler aucune méthode avecsys.stdout = Null donc dans certains cas, cela peut être dangereux.
Alexander Chzhen
Dans ce cas précis, il est connu pour être dangereux. OP traite spécifiquement l'erreur qu'il obtient en le faisant.
Mad Physicist
0

Supplément à la réponse d' iFreilicht - cela fonctionne à la fois pour python 2 et 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
la source