¿Cómo vaciar la salida de la función de impresión?

1218

¿Cómo fuerzo la función de impresión de Python para que salga a la pantalla?

Este no es un duplicado de Deshabilitar el almacenamiento en búfer de salida : la pregunta vinculada es intentar la salida sin búfer, mientras que esto es más general. Las principales respuestas en esa pregunta son demasiado poderosas o involucradas para esta (no son buenas respuestas para esto), y esta pregunta puede ser encontrada en Google por un novato relativo.

Walter Nissen
fuente

Respuestas:

1430

En Python 3, printpuede tomar un flushargumento opcional

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

En Python 2 tendrás que hacer

import sys
sys.stdout.flush()

despues de llamar print. De forma predeterminada, printimprime en sys.stdout(consulte la documentación para obtener más información sobre los objetos de archivo ).

CesarB
fuente
347

En ejecución python -h, veo una opción de línea de comando :

-u: stdout binario sin búfer y stderr; también PYTHONUNBUFFERED = x consulte la página de manual para obtener detalles sobre el almacenamiento intermedio interno relacionado con '-u'

Aquí está el documento relevante .

gimel
fuente
321

Desde Python 3.3, puede forzar el vaciado de la print()función normal sin la necesidad de usar sys.stdout.flush(); simplemente establezca el argumento de la palabra clave "flush" en verdadero. De la documentación :

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

Imprima objetos en el archivo continuo, separados por sep y seguidos por final. sep, end y file, si están presentes, deben darse como argumentos de palabras clave.

Todos los argumentos que no son palabras clave se convierten en cadenas como str () y se escriben en la secuencia, separados por sep y seguidos por end. Tanto sep como end deben ser cadenas; También pueden ser Ninguno, lo que significa utilizar los valores predeterminados. Si no se dan objetos, print () simplemente escribirá end.

El argumento del archivo debe ser un objeto con un método de escritura (cadena); si no está presente o Ninguno, se usará sys.stdout. Si el resultado se almacena en búfer generalmente se determina por archivo, pero si el argumento de la palabra clave flush es verdadero, el flujo se vacía a la fuerza.

Eugene Sajine
fuente
199

¿Cómo vaciar la salida de la impresión Python?

Sugiero cinco formas de hacer esto:

  • En Python 3, llame print(..., flush=True)(el argumento de vaciado no está disponible en la función de impresión de Python 2, y no hay análogo para la declaración de impresión).
  • Llame file.flush()al archivo de salida (podemos ajustar la función de impresión de python 2 para hacer esto), por ejemplo,sys.stdout
  • aplique esto a cada llamada a la función de impresión en el módulo con una función parcial,
    print = partial(print, flush=True)aplicada al módulo global.
  • aplicar esto al proceso con una bandera ( -u) pasada al comando del intérprete
  • aplique esto a cada proceso de Python en su entorno con PYTHONUNBUFFERED=TRUE(y desarme la variable para deshacer esto).

Python 3.3+

Con Python 3.3 o superior, solo puede proporcionar flush=Truecomo argumento de palabra clave a la printfunción:

print('foo', flush=True) 

Python 2 (o <3.3)

No respaldaron el flushargumento de Python 2.7. Por lo tanto, si está utilizando Python 2 (o menos de 3.3) y desea un código que sea compatible con 2 y 3, ¿puedo sugerir el siguiente código de compatibilidad? (Tenga en cuenta que la __future__importación debe estar en / muy "cerca de la parte superior de su módulo "):

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

El código de compatibilidad anterior cubrirá la mayoría de los usos, pero para un tratamiento mucho más completo, consulte el sixmódulo .

Alternativamente, puede llamar file.flush()después de imprimir, por ejemplo, con la declaración de impresión en Python 2:

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

Cambiar el valor predeterminado en un módulo a flush=True

Puede cambiar el valor predeterminado para la función de impresión utilizando functools.partial en el alcance global de un módulo:

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

Si observa nuestra nueva función parcial, al menos en Python 3:

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

Podemos ver que funciona igual de normal:

>>> print('foo')
foo

Y en realidad podemos anular el nuevo valor predeterminado:

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

Tenga en cuenta nuevamente, esto solo cambia el alcance global actual, porque el nombre de impresión en el alcance global actual eclipsará la printfunción incorporada (o desreferenciará la función de compatibilidad, si usa una en Python 2, en ese alcance global actual).

Si desea hacer esto dentro de una función en lugar de en el ámbito global de un módulo, debe darle un nombre diferente, por ejemplo:

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

Si lo declara global en una función, lo está cambiando en el espacio de nombres global del módulo, por lo que debe colocarlo en el espacio de nombres global, a menos que ese comportamiento específico sea exactamente lo que desea.

Cambiar el valor predeterminado para el proceso

Creo que la mejor opción aquí es usar la -ubandera para obtener resultados sin búfer.

$ python -u script.py

o

$ python -um package.module

De los documentos :

Obliga a stdin, stdout y stderr a estar totalmente libres de búfer. En los sistemas donde importa, también ponga stdin, stdout y stderr en modo binario.

Tenga en cuenta que existe un almacenamiento intermedio interno en file.readlines () y File Objects (para la línea en sys.stdin) que no está influenciada por esta opción. Para evitar esto, querrá usar file.readline () dentro de un tiempo 1: bucle.

Cambiar el valor predeterminado para el entorno operativo de shell

Puede obtener este comportamiento para todos los procesos de Python en el entorno o entornos que heredan del entorno si establece la variable de entorno en una cadena no vacía:

por ejemplo, en Linux u OSX:

$ export PYTHONUNBUFFERED=TRUE

o Windows:

C:\SET PYTHONUNBUFFERED=TRUE

de los documentos :

PYTHONUNBUFFERED

Si se establece en una cadena no vacía, es equivalente a especificar la opción -u.


Apéndice

Aquí está la ayuda sobre la función de impresión de Python 2.7.12 - tenga en cuenta que no hay flush argumento:

>>> 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
fuente
Para los curiosos que migran desde versiones anteriores de Python: la __future__versión no incluye flushporque "el argumento de vaciado se agregó en Python 3.3 (después de que print () se exportó a 2.7 a través de una importación futura)" bugs.python.org/issue28458
Oliver
69

Además, como se sugiere en este blog, se puede volver sys.stdouta abrir en modo sin búfer:

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

Cada una stdout.writey la printoperación se enjuagarán automáticamente después.

Antony Hatchkins
fuente
2
En Ubuntu 12.04 en Python 2.7 esto me daUnsupportedOperation: IOStream has no fileno.
drevicko
3
Vaya, Python 3 descubrió. ¡No me deja ejecutar este código!
EKons
Estoy confundido por este idioma. Después de hacer esto, ¿no hay ahora dos objetos tipo File (el sys.stdout original y el nuevo sys.stdout) que ambos piensan que "poseen" el fileno? Eso es malo, ¿verdad?
Don Hatch
62

Con Python 3.x la print()función se ha ampliado:

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

Entonces, solo puedes hacer:

print("Visiting toilet", flush=True)

Entrada de documentos de Python

Noah Krasser
fuente
36

Usar el -uinterruptor de línea de comandos funciona, pero es un poco torpe. Significaría que el programa podría comportarse incorrectamente si el usuario invocara el script sin la -uopción. Usualmente uso una costumbre stdout, como esta:

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)

... Ahora todas sus printllamadas (que se usan sys.stdoutimplícitamente) serán editadas automáticamente flush.

Dan Lenski
fuente
44
Recomiendo no heredar del archivo y luego delegar en stdout agregando. def __getattr__(self,name): return object.__getattribute__(self.f, name)
murióthreetimes
2
Sin los cambios sugeridos por el comentario de @diedthreetimes, obtengo "ValueError: operación de E / S en archivo cerrado"
blueFast
19

¿Por qué no intentar usar un archivo sin búfer?

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

O

sys.stdout = open('out.log', 'a', 0)
Nakilon
fuente
1
No quiere crear un archivo sin búfer; él quiere hacer que el stdout existente (redirigido a la consola, la terminal o lo que sea: esto no se debe cambiar) sin búfer.
blueFast
13

La idea de Dan no funciona del todo:

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

El resultado:

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

Creo que el problema es que hereda de la clase de archivo, lo que en realidad no es necesario. Según los documentos para sys.stdout:

stdout y stderr no necesitan ser objetos de archivo integrados: cualquier objeto es aceptable siempre que tenga un método write () que tome un argumento de cadena.

tan cambiante

class flushfile(file):

a

class flushfile(object):

hace que funcione bien.

Kamil Kisiel
fuente
17
Sin voto porque esta es la solución de Dan @ ... (Deberías comentar la publicación de Dan en lugar de copiar su solución)
gecco
8

Aquí está mi versión, que también proporciona writelines () y 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
fuente
Solución superior Y funciona. Probado en Python 3.4.0. Con las otras versiones, que derivan de file, obtengo un error. No hay fileclase
Colin D Bennett
6

En Python 3 puede sobrescribir la función de impresión con el valor predeterminado establecido en flush = True

def print(*objects, sep=' ', end='\n', file=sys.stdout, flush=True):
    __builtins__.print(*objects, sep=sep, end=end, file=file, flush=flush)
usuario263387
fuente
2
Esta respuesta parece un poco ligera dado el resto de las respuestas de alta calidad. es posible que desee agregarle un poco más.
Punto y coma y cinta adhesiva
5

Lo hice así 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
fuente
2

Primero me costó entender cómo funcionaba la opción de descarga. Quería hacer una 'pantalla de carga' y aquí está la solución que encontré:

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

La primera línea vacía la impresión anterior y la segunda línea imprime un nuevo mensaje actualizado. No sé si existe una sintaxis de una línea aquí.

Guillaume Mougeot
fuente