Existe-t-il un moyen dans Python pour arrêter la sortie standard sans encapsuler un appel de fonction comme suit?
Code cassé d'origine:
from sys import stdout
from copy import copy
save_stdout = copy(stdout)
stdout = open('trash','w')
foo()
stdout = save_stdout
Edit: Code corrigé d'Alex Martelli
import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout
Cela fonctionne mais semble terriblement inefficace. Il a pour être un meilleur moyen. Des idées?
L'affectation de la variable stdout
pendant que vous effectuez n'a aucun effet, en supposant que foo
contient des instructions print
- encore un autre exemple de la raison pour laquelle vous ne devriez jamais importer des éléments de à l'intérieur d'un module (comme vous le faites ici), mais toujours un module dans son ensemble (alors utilisez des noms qualifiés). Soit dit en passant, le copy
n'est pas pertinent. L'équivalent correct de votre extrait est:
import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout
Maintenant, lorsque le code est correct, il est temps de le rendre plus élégant ou plus rapide. Par exemple, vous pouvez utiliser un objet de type fichier en mémoire au lieu du fichier "corbeille":
import sys
import io
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
foo()
sys.stdout = save_stdout
pour l'élégance, un contexte est préférable, par exemple:
import contextlib
import io
import sys
@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
yield
sys.stdout = save_stdout
une fois que vous avez défini ce contexte, pour tout bloc dans lequel vous ne voulez pas de sortie standard,
with nostdout():
foo()
Plus d'optimisation: il vous suffit de remplacer sys.stdout par un objet qui a une méthode no-op write
. Par exemple:
import contextlib
import sys
class DummyFile(object):
def write(self, x): pass
@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = DummyFile()
yield
sys.stdout = save_stdout
à utiliser de la même manière que l'implémentation précédente de nostdout
. Je ne pense pas que cela devienne plus propre ou plus rapide que cela ;-).
Juste pour ajouter à ce que d'autres ont déjà dit, Python 3.4 a introduit le contextlib.redirect_stdout
gestionnaire de contexte. Il accepte un objet fichier (-like) vers lequel la sortie doit être redirigée.
La redirection vers /dev/null supprimera la sortie:
In [11]: def f(): print('noise')
In [12]: import os, contextlib
In [13]: with open(os.devnull, 'w') as devnull:
....: with contextlib.redirect_stdout(devnull):
....: f()
....:
In [14]:
Cette solution peut être adaptée pour être utilisée comme décorateur:
import os, contextlib
def supress_stdout(func):
def wrapper(*a, **ka):
with open(os.devnull, 'w') as devnull:
with contextlib.redirect_stdout(devnull):
func(*a, **ka)
return wrapper
@supress_stdout
def f():
print('noise')
f() # nothing is printed
Une autre solution possible et parfois utile qui fonctionnera dans les deux Python 2 et 3 est de passer /dev/null comme un argument vers f
et redirigez la sortie en utilisant l'argument file
de la fonction print
:
In [14]: def f(target): print('noise', file=target)
In [15]: with open(os.devnull, 'w') as devnull:
....: f(target=devnull)
....:
In [16]:
Vous pouvez même rendre target
complètement facultatif:
def f(target=sys.stdout):
# Here goes the function definition
Notez que vous devrez
from __future__ import print_function
dans Python 2.
Pourquoi pensez-vous que c'est inefficace? L'avez-vous test cela? Soit dit en passant, cela ne fonctionne pas du tout car vous utilisez le from ... import
déclaration. Remplacement de sys.stdout
c'est bien, mais ne faites pas de copie et n'utilisez pas de fichier temporaire. Ouvrez le périphérique nul à la place:
import sys
import os
def foo():
print "abc"
old_stdout = sys.stdout
sys.stdout = open(os.devnull, "w")
try:
foo()
finally:
sys.stdout.close()
sys.stdout = old_stdout
Sonner très tard à cela avec ce que je pensais être une solution plus propre à ce problème.
import sys, traceback
class Suppressor(object):
def __enter__(self):
self.stdout = sys.stdout
sys.stdout = self
def __exit__(self, type, value, traceback):
sys.stdout = self.stdout
if type is not None:
# Do normal exception handling
def write(self, x): pass
Usage:
with Suppressor():
DoMyFunction(*args,**kwargs)
Une légère modification de réponse d'Alex Martelli ...
Cela résout le cas où vous souhaitez toujours supprimer stdout
pour une fonction au lieu d'appels individuels à la fonction.
Si foo()
était appelé plusieurs fois, il serait préférable/plus facile d'envelopper la fonction (de la décorer). De cette façon, vous modifiez la définition de foo
une fois au lieu d'encapsuler chaque utilisation de la fonction dans une instruction with.
import sys
from somemodule import foo
class DummyFile(object):
def write(self, x): pass
def nostdout(func):
def wrapper(*args, **kwargs):
save_stdout = sys.stdout
sys.stdout = DummyFile()
func(*args, **kwargs)
sys.stdout = save_stdout
return wrapper
foo = nostdout(foo)
En généralisant encore plus, vous pouvez obtenir un joli décorateur qui peut capturer la sortie et même la retourner:
import sys
import cStringIO
from functools import wraps
def mute(returns_output=False):
"""
Decorate a function that prints to stdout, intercepting the output.
If "returns_output" is True, the function will return a generator
yielding the printed lines instead of the return values.
The decorator litterally Hijack sys.stdout during each function
execution for ALL THE THREADS, so be careful with what you apply it to
and in which context.
>>> def numbers():
print "42"
print "1984"
...
>>> numbers()
42
1984
>>> mute()(numbers)()
>>> list(mute(True)(numbers)())
['42', '1984']
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
saved_stdout = sys.stdout
sys.stdout = cStringIO.StringIO()
try:
out = func(*args, **kwargs)
if returns_output:
out = sys.stdout.getvalue().strip().split()
finally:
sys.stdout = saved_stdout
return out
return wrapper
return decorator
Je ne pense pas que ça devienne plus propre ou plus rapide que ça; -)
Bah! Je pense que je peux faire un peu mieux :-D
import contextlib, cStringIO, sys
@contextlib.contextmanager
def nostdout():
'''Prevent print to stdout, but if there was an error then catch it and
print the output before raising the error.'''
saved_stdout = sys.stdout
sys.stdout = cStringIO.StringIO()
try:
yield
except Exception:
saved_output = sys.stdout
sys.stdout = saved_stdout
print saved_output.getvalue()
raise
sys.stdout = saved_stdout
Ce qui revient à ce que je voulais à l'origine, supprimer la sortie normalement mais afficher la sortie supprimée si une erreur était levée.
redirect_stdout () a été ajouté à contextlib depuis python 3.4
Pour python> = 3.4, cela devrait le faire:
import contextlib
import io
with contextlib.redirect_stdout(io.StringIO()):
foo()