python - Deshabilitar el búfer de salida
stdout buffered (15)
(He publicado un comentario, pero se perdió de alguna manera. Entonces, otra vez :)
Como noté, CPython (al menos en Linux) se comporta de manera diferente dependiendo de a dónde vaya la salida. Si va a un tty, la salida se vacía después de cada ''
/n''
Si va a una tubería / proceso, entonces está en búfer y puede usar las soluciones basadas enflush()
o la opción -u recomendada anteriormente.Ligeramente relacionado con el búfer de salida:
Si iteras sobre las líneas en la entrada confor line in sys.stdin:
...
luego, la implementación para CPython recopilará la entrada por un tiempo y luego ejecutará el cuerpo del bucle para un grupo de líneas de entrada. Si su secuencia de comandos está a punto de escribir la salida para cada línea de entrada, esto podría parecer un búfer de salida, pero en realidad es un proceso por lotes, y por lo tanto, ninguna de las técnicas de flush()
, etc. ayudará. Curiosamente, no tienes este comportamiento en pypy . Para evitar esto, puedes usar
while True: line=sys.stdin.readline()
...
¿El búfer de salida está habilitado de forma predeterminada en el intérprete de Python para sys.stdout
?
Si la respuesta es positiva, ¿cuáles son todas las formas de desactivarla?
Sugerencias hasta ahora:
- Utilice el interruptor de línea de comando
-u
- Envuelva
sys.stdout
en un objeto que se vacía después de cada escritura - Set
PYTHONUNBUFFERED
env var -
sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0)
¿Hay alguna otra forma de establecer un indicador global en sys
/ sys.stdout
programación durante la ejecución?
De Magnus Lycka responda en una lista de correo :
Puede omitir el almacenamiento en búfer para todo un proceso de Python utilizando "python -u" (o #! / Usr / bin / env python -u, etc.) o configurando la variable de entorno PYTHONUNBUFFERED.
También puede reemplazar sys.stdout con alguna otra secuencia como envoltura que se vacía después de cada llamada.
class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) import sys sys.stdout = Unbuffered(sys.stdout) print ''Hello''
Es posible anular solo el método de sys.stdout
de sys.stdout
con uno que llame a flush
. La implementación del método sugerido está abajo.
def write_flush(args, w=stdout.write):
w(args)
stdout.flush()
El valor predeterminado del argumento w
mantendrá la referencia del método de write
original. Después de que se define write_flush
, la write
original puede ser anulada.
stdout.write = write_flush
El código supone que stdout
se importa de esta manera from sys import stdout
.
Esto se relaciona con la respuesta de Cristóvão D. Sousa, pero todavía no pude comentar.
Una forma directa de usar el argumento de palabra clave al flush
de Python 3 para tener siempre una salida sin búfer es:
import functools
print = functools.partial(print, flush=True)
después, la impresión siempre vaciará la salida directamente (excepto que flush=False
está dado).
Tenga en cuenta que (a) esto responde la pregunta solo parcialmente, ya que no redirige toda la salida. Pero supongo que print
es la forma más común de crear una salida en stdout
/ stderr
en python, por lo que estas 2 líneas cubren la mayoría de los casos de uso.
Tenga en cuenta (b) que solo funciona en el módulo / script donde lo definió. Esto puede ser bueno cuando se escribe un módulo, ya que no se sys.stdout
con sys.stdout
.
Python 2 no proporciona el argumento de flush
, pero puede emular una función de print
tipo Python 3 como se describe aquí https://.com/a/27991478/3734258 .
Los siguientes trabajos en Python 2.6, 2.7 y 3.2:
import os
import sys
buf_arg = 0
if sys.version_info[0] == 3:
os.environ[''PYTHONUNBUFFERED''] = ''1''
buf_arg = 1
sys.stdout = os.fdopen(sys.stdout.fileno(), ''a+'', buf_arg)
sys.stderr = os.fdopen(sys.stderr.fileno(), ''a+'', buf_arg)
Prefiero poner mi respuesta en ¿Cómo vaciar la salida de la impresión de Python? o en la función de impresión de Python que vacía el búfer cuando se llama? , pero como fueron marcados como duplicados de este (lo que no estoy de acuerdo), lo contestaré aquí.
Como Python 3.3 print () admite el argumento de palabra clave "flush" ( consulte la documentación ):
print(''Hello World!'', flush=True)
Puede crear un archivo sin almacenamiento y asignar este archivo a sys.stdout.
import sys
myFile= open( "a.log", "w", 0 )
sys.stdout= myFile
No puedes cambiar mágicamente la salida estándar del sistema; ya que se suministra a su programa de python por el sistema operativo.
Sí lo es.
Puede deshabilitarlo en la línea de comandos con el interruptor "-u".
Alternativamente, puede llamar a .flush () en sys.stdout en cada escritura (o envolverlo con un objeto que haga esto automáticamente)
Sí, está habilitado por defecto. Puedes deshabilitarlo usando la opción -u en la línea de comandos cuando llames a python.
También puede ejecutar Python con la utilidad stdbuf :
stdbuf -oL python <script>
También puede usar fcntl para cambiar las marcas de archivo sobre la marcha.
fl = fcntl.fcntl(fd.fileno(), fcntl.F_GETFL)
fl |= os.O_SYNC # or os.O_DSYNC (if you don''t care the file timestamp updates)
fcntl.fcntl(fd.fileno(), fcntl.F_SETFL, fl)
Una forma de obtener una salida sin búfer sería usar sys.stderr
lugar de sys.stdout
o simplemente llamar a sys.stdout.flush()
para forzar explícitamente que se produzca una escritura.
Podrías redirigir fácilmente todo lo impreso haciendo:
import sys; sys.stdout = sys.stderr
print "Hello World!"
O para redirigir solo para una declaración de print
particular:
print >>sys.stderr, "Hello World!"
Para restablecer stdout puedes simplemente hacer:
sys.stdout = sys.__stdout__
Variante que funciona sin fallar (al menos en win32; python 2.7, ipython 0.12) y luego llamada (varias veces):
def DisOutBuffering():
if sys.stdout.name == ''<stdout>'':
sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0)
if sys.stderr.name == ''<stderr>'':
sys.stderr = os.fdopen(sys.stderr.fileno(), ''w'', 0)
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0)
Créditos: "Sebastian", en algún lugar de la lista de correo de Python.
def disable_stdout_buffering():
# Appending to gc.garbage is a way to stop an object from being
# destroyed. If the old sys.stdout is ever collected, it will
# close() stdout, which is not good.
gc.garbage.append(sys.stdout)
sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0)
# Then this will give output in the correct order:
disable_stdout_buffering()
print "hello"
subprocess.call(["echo", "bye"])
Sin guardar el antiguo sys.stdout, disable_stdout_buffering () no es idempotente, y varias llamadas darán como resultado un error como este:
Traceback (most recent call last):
File "test/buffering.py", line 17, in <module>
print "hello"
IOError: [Errno 9] Bad file descriptor
close failed: [Errno 9] Bad file descriptor
Otra posibilidad es:
def disable_stdout_buffering():
fileno = sys.stdout.fileno()
temp_fd = os.dup(fileno)
sys.stdout.close()
os.dup2(temp_fd, fileno)
os.close(temp_fd)
sys.stdout = os.fdopen(fileno, "w", 0)
(Anexar a gc.garbage no es una buena idea porque es donde se ponen los ciclos que no se pueden ver, y es posible que desee verificarlos).