Comment vider la sortie de la fonction d'impression?

1218

Comment forcer la fonction d'impression de Python à s'afficher à l'écran?

Ce n'est pas un doublon de Désactiver la mise en mémoire tampon de sortie - la question liée tente une sortie sans tampon , alors que c'est plus général. Les meilleures réponses à cette question sont trop puissantes ou impliquées pour celle-ci (ce ne sont pas de bonnes réponses pour cela), et cette question peut être trouvée sur Google par un débutant.

Walter Nissen
la source

Réponses:

1430

Sur Python 3, printpeut prendre un flushargument facultatif

print("Hello world!", flush=True)

Sur Python 2, vous devrez faire

import sys
sys.stdout.flush()

après avoir appelé print. Par défaut, printimprime vers sys.stdout(voir la documentation pour en savoir plus sur les objets fichier ).

CesarB
la source
347

En cours d'exécution python -h, je vois une option de ligne de commande :

-u: stdout et stderr binaires non tamponnés; également PYTHONUNBUFFERED = x voir la page de manuel pour plus de détails sur la mise en mémoire tampon interne relative à '-u'

Voici le doc pertinent .

gimel
la source
321

Depuis Python 3.3, vous pouvez forcer la print()fonction normale à vider sans avoir besoin d'utiliser sys.stdout.flush(); définissez simplement l'argument de mot clé "flush" sur true. De la documentation :

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

Imprimer des objets dans le fichier de flux, séparés par sep et suivis de fin. sep, end et file, s'ils sont présents, doivent être donnés comme arguments de mots clés.

Tous les arguments non mot-clés sont convertis en chaînes comme str () le font et écrits dans le flux, séparés par sep et suivis de fin. Sep et end doivent tous deux être des chaînes; ils peuvent également être None, ce qui signifie utiliser les valeurs par défaut. Si aucun objet n'est donné, print () écrira simplement end.

L'argument fichier doit être un objet avec une méthode d'écriture (chaîne); s'il n'est pas présent ou Aucun, sys.stdout sera utilisé. La sortie du tampon est généralement déterminée par fichier, mais si l'argument du mot clé flush est vrai, le flux est vidé de force.

Eugene Sajine
la source
199

Comment vider la sortie de l'impression Python?

Je suggère cinq façons de procéder:

  • Dans Python 3, appelez print(..., flush=True) (l'argument flush n'est pas disponible dans la fonction d'impression de Python 2, et il n'y a pas d'analogue pour l'instruction print).
  • Appelez file.flush()le fichier de sortie (nous pouvons envelopper la fonction d'impression de python 2 pour ce faire), par exemple,sys.stdout
  • appliquer cela à chaque appel de fonction d'impression dans le module avec une fonction partielle,
    print = partial(print, flush=True)appliquée au module global.
  • appliquer cela au processus avec un flag ( -u) passé à la commande interprète
  • appliquez ceci à chaque processus python de votre environnement avec PYTHONUNBUFFERED=TRUE(et désactivez la variable pour l'annuler).

Python 3.3+

En utilisant Python 3.3 ou supérieur, vous pouvez simplement fournir flush=Truecomme argument de mot clé à la printfonction:

print('foo', flush=True) 

Python 2 (ou <3.3)

Ils n'ont pas rétroporté l' flushargument vers Python 2.7 Donc, si vous utilisez Python 2 (ou moins de 3,3), et que vous voulez du code compatible avec 2 et 3, puis-je suggérer le code de compatibilité suivant. (Notez que l' __future__importation doit être à / très "près du haut de votre module "):

from __future__ import print_function
import sys

if sys.version_info[:2] < (3, 3):
    old_print = print
    def print(*args, **kwargs):
        flush = kwargs.pop('flush', False)
        old_print(*args, **kwargs)
        if flush:
            file = kwargs.get('file', sys.stdout)
            # Why might file=None? IDK, but it works for print(i, file=None)
            file.flush() if file is not None else sys.stdout.flush()

Le code de compatibilité ci-dessus couvrira la plupart des utilisations, mais pour un traitement beaucoup plus approfondi, consultez le sixmodule .

Alternativement, vous pouvez simplement appeler file.flush()après l'impression, par exemple, avec l'instruction print en Python 2:

import sys
print 'delayed output'
sys.stdout.flush()

Changer la valeur par défaut d'un module en flush=True

Vous pouvez modifier la valeur par défaut de la fonction d'impression en utilisant functools.partial sur la portée globale d'un module:

import functools
print = functools.partial(print, flush=True)

si vous regardez notre nouvelle fonction partielle, au moins en Python 3:

>>> print = functools.partial(print, flush=True)
>>> print
functools.partial(<built-in function print>, flush=True)

Nous pouvons voir que cela fonctionne comme d'habitude:

>>> print('foo')
foo

Et nous pouvons en fait remplacer la nouvelle valeur par défaut:

>>> print('foo', flush=False)
foo

Notez à nouveau que cela ne modifie que la portée globale actuelle, car le nom d'impression sur la portée globale actuelle éclipsera la printfonction intégrée (ou annulera la référence de la fonction de compatibilité, si vous en utilisez une en Python 2, dans cette portée globale actuelle).

Si vous souhaitez le faire à l'intérieur d'une fonction plutôt que sur la portée globale d'un module, vous devez lui donner un nom différent, par exemple:

def foo():
    printf = functools.partial(print, flush=True)
    printf('print stuff like this')

Si vous le déclarez global dans une fonction, vous le changez dans l'espace de noms global du module, vous devez donc simplement le mettre dans l'espace de noms global, à moins que ce comportement spécifique ne soit exactement ce que vous voulez.

Modification de la valeur par défaut du processus

Je pense que la meilleure option ici est d'utiliser le -udrapeau pour obtenir une sortie sans tampon.

$ python -u script.py

ou

$ python -um package.module

De la documentation :

Force stdin, stdout et stderr à être totalement sans tampon. Sur les systèmes où cela est important, mettez également stdin, stdout et stderr en mode binaire.

Notez qu'il existe un tampon interne dans file.readlines () et File Objects (pour la ligne dans sys.stdin) qui n'est pas influencé par cette option. Pour contourner ce problème, vous souhaiterez utiliser file.readline () dans une boucle while 1 :.

Modification de la valeur par défaut pour l'environnement d'exploitation du shell

Vous pouvez obtenir ce comportement pour tous les processus python de l'environnement ou des environnements qui héritent de l'environnement si vous définissez la variable d'environnement sur une chaîne non vide:

par exemple, sous Linux ou OSX:

$ export PYTHONUNBUFFERED=TRUE

ou Windows:

C:\SET PYTHONUNBUFFERED=TRUE

à partir des documents :

PYTHONUNBUFFERED

Si ce paramètre est défini sur une chaîne non vide, cela équivaut à spécifier l'option -u.


Addenda

Voici l'aide sur la fonction d'impression de Python 2.7.12 - notez qu'il n'y a pas d' flush argument:

>>> from __future__ import print_function
>>> help(print)
print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout)

    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file: a file-like object (stream); defaults to the current sys.stdout.
    sep:  string inserted between values, default a space.
    end:  string appended after the last value, default a newline.
Aaron Hall
la source
Pour les curieux migrant à partir de versions Python inférieures: la __future__version ne comprend pas flushcar "l'argument flush a été ajouté dans Python 3.3 (après que print () a été rétroporté vers 2.7 via une future importation)" bugs.python.org/issue28458
Oliver
69

Aussi, comme suggéré dans ce blog, on peut rouvrir sys.stdouten mode sans tampon:

sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

Chaque opération stdout.writeet printsera automatiquement rincée par la suite.

Antony Hatchkins
la source
2
Sur Ubuntu 12.04 en python 2.7, cela me donneUnsupportedOperation: IOStream has no fileno.
drevicko
3
Oups, Python 3 l'a découvert. Cela ne me permettra pas d'exécuter ce morceau de code!
EKons
Je suis confus par cet idiome. Après cela, n'y a-t-il pas maintenant deux objets de type fichier (le sys.stdout d'origine et le nouveau sys.stdout) qui pensent tous deux qu'ils "possèdent" le fileno? C'est mauvais, non?
Don Hatch
62

Avec Python 3.x, la print()fonction a été étendue:

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

Donc, vous pouvez simplement faire:

print("Visiting toilet", flush=True)

Entrée Python Docs

Noah Krasser
la source
36

L'utilisation du -ucommutateur de ligne de commande fonctionne, mais c'est un peu maladroit. Cela signifierait que le programme se comporterait potentiellement de manière incorrecte si l'utilisateur invoquait le script sans l' -uoption. J'utilise habituellement une coutume stdout, comme ceci:

class flushfile:
  def __init__(self, f):
    self.f = f

  def write(self, x):
    self.f.write(x)
    self.f.flush()

import sys
sys.stdout = flushfile(sys.stdout)

... Maintenant, tous vos printappels (qui utilisent sys.stdoutimplicitement), seront automatiquement flushédités.

Dan Lenski
la source
4
Je recommande de ne pas hériter du fichier, puis de déléguer à stdout en ajoutant. def __getattr__(self,name): return object.__getattribute__(self.f, name)
deadthreetimes
2
Sans les changements suggérés par le commentaire de @diedthreetimes, j'obtiens "ValueError: opération d'E / S sur fichier fermé"
blueFast
19

Pourquoi ne pas essayer d'utiliser un fichier sans tampon?

f = open('xyz.log', 'a', 0)

OU

sys.stdout = open('out.log', 'a', 0)
Nakilon
la source
1
Il ne veut pas créer un fichier sans tampon; il veut rendre la stdout existante (redirigée vers la console, le terminal ou autre: cela ne doit pas être modifié) sans tampon.
blueFast
13

L'idée de Dan ne fonctionne pas tout à fait:

#!/usr/bin/env python
class flushfile(file):
    def __init__(self, f):
        self.f = f
    def write(self, x):
        self.f.write(x)
        self.f.flush()

import sys
sys.stdout = flushfile(sys.stdout)

print "foo"

Le résultat:

Traceback (most recent call last):
  File "./passpersist.py", line 12, in <module>
    print "foo"
ValueError: I/O operation on closed file

Je crois que le problème est qu'il hérite de la classe de fichiers, ce qui n'est pas vraiment nécessaire. Selon les documents pour sys.stdout:

stdout et stderr n'ont pas besoin d'être des objets fichier intégrés: tout objet est acceptable tant qu'il a une méthode write () qui prend un argument de chaîne.

si changeant

class flushfile(file):

à

class flushfile(object):

le fait fonctionner très bien.

Kamil Kisiel
la source
17
Pas de vote parce que c'est la solution d'IS @ Dan ... (Vous devriez plutôt commenter le post de Dan au lieu de copier sa solution)
gecco
8

Voici ma version, qui fournit également writelines () et fileno ():

class FlushFile(object):
    def __init__(self, fd):
        self.fd = fd

    def write(self, x):
        ret = self.fd.write(x)
        self.fd.flush()
        return ret

    def writelines(self, lines):
        ret = self.writelines(lines)
        self.fd.flush()
        return ret

    def flush(self):
        return self.fd.flush

    def close(self):
        return self.fd.close()

    def fileno(self):
        return self.fd.fileno()
guettli
la source
Solution supérieure. Et il fonctionne. Testé sur Python 3.4.0. Avec les autres versions, qui dérivent de file, j'obtiens une erreur. Il n'y a pas de filecours.
Colin D Bennett du
6

Dans Python 3, vous pouvez remplacer la fonction d'impression avec la valeur par défaut définie sur flush = True

def print(*objects, sep=' ', end='\n', file=sys.stdout, flush=True):
    __builtins__.print(*objects, sep=sep, end=end, file=file, flush=flush)
user263387
la source
2
cette réponse semble un peu légère compte tenu de toutes les autres réponses de haute qualité. vous voudrez peut-être en ajouter un peu plus.
points
5

Je l'ai fait comme ça en Python 3.4:

'''To write to screen in real-time'''
message = lambda x: print(x, flush=True, end="")
message('I am flushing out now...')
kmario23
la source
2

J'ai d'abord eu du mal à comprendre comment fonctionnait l'option de vidage. Je voulais faire un 'affichage de chargement' et voici la solution que j'ai trouvée:

for i in range(100000):
    print('{:s}\r'.format(''), end='', flush=True)
    print('Loading index: {:d}/100000'.format(i+1), end='')

La première ligne efface l'impression précédente et la deuxième ligne imprime un nouveau message mis à jour. Je ne sais pas si une syntaxe sur une ligne existe ici.

Guillaume Mougeot
la source