Comment imprimer sur stderr en Python?

1341

Il y a plusieurs façons d'écrire sur stderr:

# Note: this first one does not work in Python 3
print >> sys.stderr, "spam"

sys.stderr.write("spam\n")

os.write(2, b"spam\n")

from __future__ import print_function
print("spam", file=sys.stderr)

Cela semble contredire le zen de Python # 13 , alors quelle est la différence ici et y a-t-il des avantages ou des inconvénients d'une manière ou d'une autre? Quelle voie utiliser?

Il devrait y avoir une - et de préférence une seule - façon évidente de le faire.

wim
la source
46
La première façon répertoriée est l'une des nombreuses choses supprimées dans Python 3. Le consensus semble être que la >> syntaxe était laide de toute façon, et puisque print est maintenant une fonction, la syntaxe ne fonctionnerait jamais.
Steve Howard
24
J'utilise: sys.exit ('Erreur: <texte d'erreur>')
Stark
1
utilisez simplement l'impression.
PythonMaster202

Réponses:

1166

J'ai trouvé que c'était le seul court + flexible + portable + lisible:

from __future__ import print_function
import sys

def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)

La fonction eprintpeut être utilisée de la même manière que la printfonction standard :

>>> print("Test")
Test
>>> eprint("Test")
Test
>>> eprint("foo", "bar", "baz", sep="---")
foo---bar---baz
Mars
la source
29
Une seule pensée: puisque cela importera la fonction d'impression, toutes les autres "impressions" dans le script d'origine devront maintenant être "fonctionnalisées" en ajoutant "(" et ")". Voilà donc une légère grève contre cette méthode, l'OMI.
Dan H
46
@DanH Oui, cela vous oblige à rendre votre code prêt pour Python3. Je suppose que cela pourrait être la raison pour laquelle beaucoup de gens l'aiment!
MarcH
18
@MarkH ... oui, cela vous oblige à rendre votre code prêt pour Python3 ... cela vous oblige également à le faire MAINTENANT, juste pour imprimer quelques informations de débogage sur le stderr ... dans lequel je trouverais plus de tracas dans la plupart des situations où j'essaie de déboguer quelque chose. (Je préfère ne pas introduire de nouvelles erreurs de syntaxe!) :-)
Dan H
30
FWIW ce code ne vous oblige pas à utiliser la version de fonction de printdans tout votre programme. Uniquement dans le module contenant la définition de eprint(). Mettez-le dans un petit fichier par lui-même, importez- eprintle dans vos autres fichiers et vous pouvez continuer à utiliser l'instruction printaussi longtemps que vous le souhaitez.
alexis
4
De plus, la conversion de la fonction d'impression en fonction d'impression est un simple remplacement de recherche que 2to3 automatise déjà pour vous. Faites-le déjà si vous ne l'avez pas fait; python2 aura disparu dans moins de 2 ans ... Certaines choses entre 2 et 3 peuvent devenir un peu délicates; la fonction d'impression n'en fait pas partie. Voir docs.python.org/2/library/2to3.html
bobpaul
548
import sys
sys.stderr.write()

Est mon choix, juste plus lisible et dit exactement ce que vous avez l'intention de faire et portable sur plusieurs versions.

Edit: être 'pythonic' est une troisième pensée pour moi sur la lisibilité et les performances ... avec ces deux choses à l'esprit, avec python 80% de votre code sera pythonic. la compréhension de la liste étant la «grande chose» qui n'est pas utilisée aussi souvent (lisibilité).

Mike Ramirez
la source
88
N'oubliez pas de rincer.
temoto du
10
L'avantage de l' printinstruction est l'impression facile des valeurs non-chaîne, sans avoir à les convertir d'abord. Si vous avez besoin d'une instruction print, je recommanderais donc d'utiliser la 3ème option pour être prêt pour python 3
vdboor
56
sys.stderr.write()n'a rien à voir print. Il n'ajoute pas de nouvelle ligne.
Colonel Panic
41
@temoto - stderr n'est pas mis en mémoire tampon, donc pas besoin de vider.
Matt
11
@ SkipHuffman Vous voulez dire os.linesep. C'est de stderrcela dont nous parlons, après tout. Je ne veux pas que la console se trompe avec la mauvaise nouvelle ligne.
jpmc26
182

print >> sys.stderrest parti en Python3. http://docs.python.org/3.0/whatsnew/3.0.html dit:

Old: print >> sys.stderr, "fatal error"
New: print("fatal error", file=sys.stderr)

Pour beaucoup d'entre nous, il semble quelque peu contre nature de reléguer la destination à la fin de la commande. L'alternative

sys.stderr.write("fatal error\n")

semble plus orienté objet et passe avec élégance du générique au spécifique. Mais notez que ce writen'est pas un remplacement 1: 1 pour print.

Joachim W
la source
6
Je suppose que c'est une question de préférence, mais je ne vois pas ce qui est laid print('spam', file=sys.stderr). Si vous le faites encore et encore, vous pouvez coder la fonction «eprint» comme dans la réponse la plus populaire, mais dans ce cas, je demanderais, qu'est-ce qui ne va pas avec la journalisation? stackoverflow.com/a/41304513/1450294
Michael Scheper
1
Une autre façon de clarifier l'intention serait de le faire with sys.stderr as dest:avant un appel en retrait àprint("ERROR", file=dest)
MarkHu
131

Personne n'est loggingencore mentionné , mais la journalisation a été créée spécifiquement pour communiquer les messages d'erreur. La configuration de base mettra en place un gestionnaire de flux écrivant sur stderr.

Ce script:

# foo.py
import logging

logging.basicConfig(format='%(message)s')
log = logging.getLogger(__name__)
log.warning('I print to stderr by default')
print('hello world')

a le résultat suivant lorsqu'il est exécuté sur la ligne de commande:

$ python3 foo.py > bar.txt
I print to stderr by default

et bar.txt contiendra le «bonjour du monde» imprimé sur stdout.

détrempé
la source
3
D'après mon expérience, plus de personnes utilisent l'impression pour enregistrer les messages que la journalisation. Je pense que python4 devrait simplement supprimer print du langage et vous forcer à utiliser la journalisation pour cela.
Mnebuerquo
1
C'est la meilleure réponse !! ... Je me débattais avec print ou sys ou qui sait ... quand une bonne journalisation est nécessaire ... merci pour la bonne idée
Carlos Saltos
129

Pour Python 2, mon choix est le suivant: print >> sys.stderr, 'spam' parce que vous pouvez simplement imprimer des listes / dict, etc. sans les convertir en chaîne. print >> sys.stderr, {'spam': 'spam'} au lieu de: sys.stderr.write(str({'spam': 'spam'}))

Frankovskyi Bogdan
la source
6
La façon la plus pythonique d'imprimer un dictionnaire serait de "{0}".format({'spam': 'spam'})toute façon, n'est-ce pas? Je dirais que vous devriez éviter de convertir explicitement en chaîne. Edit: j'ai accidentellement une grammaire
luketparkinson
2
@luketparkinson tout cela sur le débogage - donc, je pense, il est préférable d'utiliser le code le plus simple possible.
Frankovskyi Bogdan
88
Cela ne fonctionne pas sur Python 3, vous devez donc l'éviter dans le nouveau code.
JonnyJD
33

J'ai fait ce qui suit en utilisant Python 3:

from sys import stderr

def print_err(*args, **kwargs):
    print(*args, file=stderr, **kwargs)

Alors maintenant, je peux ajouter des arguments de mots clés, par exemple, pour éviter le retour chariot:

print_err("Error: end of the file reached. The word ", end='')
print_err(word, "was not found")
aaguirre
la source
2
J'allais suggérer que vous pourriez aussi utiliser un partiel, mais j'ai réalisé que le partiel assigne le stderr à la fonction au moment de la création du partiel. Cela vous empêche de rediriger stderr plus tard car le partiel contiendra toujours l'objet stderr d'origine.
Rebs
31

Je dirais que votre première approche:

print >> sys.stderr, 'spam' 

est le "Un ... moyen évident de le faire" Les autres ne satisfont pas à la règle n ° 1 ("Beau vaut mieux que laid.")

Carl F.
la source
109
Les opinions diffèrent. C'est le moins évident pour moi.
porgarmingduod
4
@AliVeli Il n'y a pas de parenthèses, il s'agit d'une ancienne syntaxe Python <= 2, et donc non compatible Python 3.
Rebs
30
Je dirais que c'est la version la plus laide de tous les 3
volcan
4
Qu'est-ce >>que cela signifie syntaxiquement? Je comprends que c'est un effort pour copier des bash >, alors est-ce une syntaxe chausse-pied pour faire exactement cela?
Dmytro Sirenko
2
@EarlGray C'est un maintien de l'opérateur d'insertion de flux de C ++:std::cout << "spam";
Sean Allred
19

Cela imitera la fonction d'impression standard mais sortira sur stderr

def print_err(*args):
    sys.stderr.write(' '.join(map(str,args)) + '\n')
Brian W.
la source
8
J'ajouterais un sys.stderr.flush ()
AMS
5
@AMS - Pourquoi? printn'inclut pas de couleur.
Robᵩ
4
Pourquoi imiter quand vous pouvez réellement le faire?
Mad Physicist
19

En Python 3, on peut simplement utiliser print ():

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

presque sorti de la boîte:

import sys
print("Hello, world!", file=sys.stderr)

ou:

from sys import stderr
print("Hello, world!", file=stderr)

C'est simple et n'a pas besoin d'inclure quoi que ce soit d'autre sys.stderr.

Florian Castellane
la source
16

ÉDITER Avec le recul, je pense que la confusion potentielle avec le changement de sys.stderr et le fait de ne pas voir le comportement mis à jour rend cette réponse pas aussi bonne que l'utilisation d'une fonction simple comme d'autres l'ont souligné.

L'utilisation de partiel ne vous fait économiser qu'une ligne de code. La confusion potentielle ne vaut pas la peine d'enregistrer 1 ligne de code.

original

Pour le rendre encore plus facile, voici une version qui utilise «partiel», ce qui est d'une grande aide dans les fonctions d'emballage.

from __future__ import print_function
import sys
from functools import partial

error = partial(print, file=sys.stderr)

Vous l'utilisez ensuite comme ça

error('An error occured!')

Vous pouvez vérifier qu'il imprime sur stderr et non stdout en procédant comme suit (en remplaçant le code de http://coreygoldberg.blogspot.com.au/2009/05/python-redirect-or-turn-off-stdout-and .html ):

# over-ride stderr to prove that this function works.
class NullDevice():
    def write(self, s):
        pass
sys.stderr = NullDevice()

# we must import print error AFTER we've removed the null device because
# it has been assigned and will not be re-evaluated.
# assume error function is in print_error.py
from print_error import error

# no message should be printed
error("You won't see this error!")

L'inconvénient de ceci est partiels ayants la valeur de sys.stderr à la fonction enroulée au moment de la création. Ce qui signifie que si vous redirigez stderr plus tard, cela n'affectera pas cette fonction. Si vous prévoyez de rediriger stderr, utilisez la méthode ** kwargs mentionnée par aaguirre sur cette page.

Rebs
la source
Le code de Corey Goldberg est-il mieux exécuté sur une machine Rube Goldberg? : P
Agi Hammerthief
2
BTW: "currying" est un mot-clé de recherche (plus) utile si vous voulez en savoir plus sur "partial".
MarcH
5

Il en va de même pour la sortie standard:

print 'spam'
sys.stdout.write('spam\n')

Comme indiqué dans les autres réponses, l' impression offre une jolie interface qui est souvent plus pratique (par exemple pour imprimer des informations de débogage), tandis que l' écriture est plus rapide et peut également être plus pratique lorsque vous devez formater la sortie exactement d'une certaine manière. Je considérerais également la maintenabilité:

  1. Vous pouvez ensuite décider de basculer entre stdout / stderr et un fichier normal.

  2. La syntaxe de print () a changé en Python 3, donc si vous avez besoin de prendre en charge les deux versions, write () pourrait être mieux.

Seppo Enarvi
la source
4
L'utilisation from __future__ import print_functionest une meilleure façon de prendre en charge Python 2.6+ et Python 3.
phoenix
4

Je travaille en python 3.4.3. Je découpe un petit texte qui montre comment je suis arrivé ici:

[18:19 jsilverman@JSILVERMAN-LT7 pexpect]$ python3
>>> import sys
>>> print("testing", file=sys.stderr)
testing
>>>
[18:19 jsilverman@JSILVERMAN-LT7 pexpect]$ 

Cela a-t-il fonctionné? Essayez de rediriger stderr vers un fichier et voyez ce qui se passe:

[18:22 jsilverman@JSILVERMAN-LT7 pexpect]$ python3 2> /tmp/test.txt
>>> import sys
>>> print("testing", file=sys.stderr)
>>> [18:22 jsilverman@JSILVERMAN-LT7 pexpect]$
[18:22 jsilverman@JSILVERMAN-LT7 pexpect]$ cat /tmp/test.txt
Python 3.4.3 (default, May  5 2015, 17:58:45)
[GCC 4.9.2] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
testing

[18:22 jsilverman@JSILVERMAN-LT7 pexpect]$

Eh bien, à part le fait que la petite introduction que python vous donne a été insinuée dans stderr (où cela irait-il?), Cela fonctionne.

user1928764
la source
2

Si vous faites un test simple:

import time
import sys

def run1(runs):
    x = 0
    cur = time.time()
    while x < runs:
        x += 1
        print >> sys.stderr, 'X'
    elapsed = (time.time()-cur)
    return elapsed

def run2(runs):
    x = 0
    cur = time.time()
    while x < runs:
        x += 1
        sys.stderr.write('X\n')
        sys.stderr.flush()
    elapsed = (time.time()-cur)
    return elapsed

def compare(runs):
    sum1, sum2 = 0, 0
    x = 0
    while x < runs:
        x += 1
        sum1 += run1(runs)
        sum2 += run2(runs)
    return sum1, sum2

if __name__ == '__main__':
    s1, s2 = compare(1000)
    print "Using (print >> sys.stderr, 'X'): %s" %(s1)
    print "Using (sys.stderr.write('X'),sys.stderr.flush()):%s" %(s2)
    print "Ratio: %f" %(float(s1) / float(s2))

Vous constaterez que sys.stderr.write () est toujours 1,81 fois plus rapide!

ThePracticalOne
la source
Si je lance cela, je vois une différence beaucoup plus petite. Il est intéressant de noter que la plupart des réponses ignorent la fonction print (python 3). Je ne l'ai jamais utilisé auparavant (inertie), mais j'ai pensé exécuter ce script de synchronisation et ajouter la fonction d'impression. La comparaison directe de l'instruction d'impression et de la fonction n'est pas possible (l'importation du futur s'applique à l'ensemble du fichier et masque l'instruction d'impression) mais en réécrivant ce code pour utiliser la fonction d'impression au lieu de l'instruction, je vois une accélération plus importante (~ 1,6 bien que quelque peu variable ) en faveur de la fonction d'impression.
hamish
1
Le résultat de ce test est en quelque sorte trompeur. Imprimez «XXXXXXXXXXXXXXXXXXXX» au lieu de «X» et le rapport tombe à 1,05 . Je suppose que la plupart des programmes python doivent imprimer plus d'un seul caractère.
Toujours demander le
15
Je ne me soucie pas des performances, pour quelque chose comme l'impression des avertissements.
wim
Je sais que cela fait un moment, mais vous avez répondu tout aussi longtemps après mon message ... Si vous ne vous souciez pas des performances, je suggérerais que la manière la plus pythonique serait d'utiliser sys.stderr.write et non le WTF?!? Caractères ">>". Si cet espace de noms sys.stdout est trop long, vous pouvez le renommer ... (c'est-à-dire de sys import stderr en stderr_fh). Ensuite, vous pouvez faire stderr_fh.write ("blah")
ThePracticalOne
1
[3/3] Même si cette référence était plus précise, cela ne vaut probablement pas la peine de s'inquiéter. Comme l'écrivait Knuth: "Les programmeurs perdent énormément de temps à penser ou à s'inquiéter de la vitesse des parties non critiques de leurs programmes, et ces tentatives d'efficacité ont en fait un fort impact négatif lorsque le débogage et la maintenance sont envisagés. Nous devons oublier les petits efficacité, disons environ 97% du temps: l'optimisation prématurée est la racine de tout mal. "
Corey Goldberg
1

Si vous souhaitez quitter un programme en raison d'une erreur fatale, utilisez:

sys.exit("Your program caused a fatal error. ... description ...")

et import sysdans l'en-tête.

feli_x
la source
-2

La réponse à la question est: Il existe différentes manières d'imprimer stderr en python mais cela dépend de 1.) quelle version de python nous utilisons 2.) quelle sortie exacte nous voulons.

La différence entre l'impression et la fonction d'écriture de stderr : stderr : stderr (erreur standard) est un canal intégré à chaque système UNIX / Linux, lorsque votre programme se bloque et imprime des informations de débogage (comme une traceback en Python), il va au stderr tuyau.

impression : print est un wrapper qui formate les entrées (l'entrée est l'espace entre l'argument et la nouvelle ligne à la fin) et appelle ensuite la fonction d'écriture d'un objet donné, l'objet donné par défaut est sys.stdout, mais nous pouvons passer un fichier, c'est-à-dire que nous pouvons également imprimer l'entrée dans un fichier.

Python2: Si nous utilisons python2,

>>> import sys
>>> print "hi"
hi
>>> print("hi")
hi
>>> print >> sys.stderr.write("hi")
hi

En Python3, la virgule de fin Python2 est devenue un paramètre, donc si nous utilisons des virgules de fin pour éviter la nouvelle ligne après une impression, cela ressemblera en Python3 à print ('Text to print', end = '') qui est une erreur de syntaxe sous Python2 .

http://python3porting.com/noconv.html

Si nous vérifions le même sceario ci-dessus en python3:

>>> import sys
>>> print("hi")
hi

Sous Python 2.6, il y a une future importation pour faire de l'impression une fonction. Donc, pour éviter toute erreur de syntaxe et autres différences, nous devons démarrer tout fichier dans lequel nous utilisons print () avec la future import print_function. La future importation ne fonctionne que sous Python 2.6 et versions ultérieures, donc pour Python 2.5 et versions antérieures, vous avez deux options. Vous pouvez soit convertir l'impression plus complexe en quelque chose de plus simple, soit utiliser une fonction d'impression distincte qui fonctionne sous Python2 et Python3.

>>> from __future__ import print_function
>>> 
>>> def printex(*args, **kwargs):
...     print(*args, file=sys.stderr, **kwargs)
... 
>>> printex("hii")
hii
>>>

Cas: Il convient de noter que sys.stderr.write () ou sys.stdout.write () (stdout (sortie standard) est un canal intégré à chaque système UNIX / Linux) ne remplace pas l'impression, mais oui nous pouvons l'utiliser comme alternative dans certains cas. Print est un wrapper qui enveloppe l'entrée avec un espace et une nouvelle ligne à la fin et utilise la fonction d'écriture pour écrire. C'est la raison pour laquelle sys.stderr.write () est plus rapide.

Remarque: nous pouvons également tracer et déboguer à l'aide de la journalisation

#test.py
import logging
logging.info('This is the existing protocol.')
FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logging.warning("Protocol problem: %s", "connection reset", extra=d)

https://docs.python.org/2/library/logging.html#logger-objects

Vinay Kumar
la source