2008-09-20 11 views
383

¿Está habilitado el almacenamiento en búfer de salida de forma predeterminada en el intérprete de Python para sys.stdout?Desactivar el almacenamiento en memoria intermedia de salida

Si la respuesta es positiva, ¿cuáles son todas las formas de deshabilitarla?

Sugerencias hasta ahora:

  1. Use el interruptor de la línea -u comando
  2. Wrap sys.stdout en un objeto que elimina después de cada escritura
  3. Establecer PYTHONUNBUFFERED env var
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

¿Hay alguna otra manera de establecer un poco de fla mundial g en sys/sys.stdout programmatically durante la ejecución?

+0

Para 'imprimir' en Python 3, ver [esta respuesta] (http://stackoverflow.com/a/14729823/918959). –

+0

Creo que un inconveniente de '-u' es que no funcionará para bytecode compilado o para aplicaciones con un archivo' __main __. Py' como punto de entrada. – akhan

Respuesta

329

De Magnus Lycka answer on a mailing list:

You can skip buffering for a whole python process using "python -u" (or#!/usr/bin/env python -u etc) or by setting the environment variable PYTHONUNBUFFERED.

You could also replace sys.stdout with some other stream like wrapper which does a flush after every call.

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' 
+62

Original sys.stdout todavía está disponible como sys .__ stdout__. En caso de que lo necesite =) –

+0

Esta es la solución que utilicé cuando me encontré con problemas con las declaraciones de impresión almacenadas. Trabajado como un encanto. – Ryan

+23

'#!/Usr/bin/env python -u' no funciona !! ver [aquí] (http://stackoverflow.com/q/3306518/674039) – wim

35

Sí, lo es.

Puede desactivarlo en la línea de comandos con el interruptor "-u".

Alternativamente, se podría llamar .flush() en sys.stdout en cada escritura (o envolverlo con un objeto que lo hace automáticamente)

+0

hermosa. Gracias. –

8

Sí, está activado por defecto. Puede deshabilitarlo utilizando la opción -u en la línea de comando al llamar a python.

4

Una forma de obtener una salida sin búfer sería usar sys.stderr en lugar de sys.stdout o simplemente llamar al sys.stdout.flush() para forzar explícitamente la escritura.

Desde aquí se puede redirigir todo lo impreso por hacer:

import sys; sys.stdout = sys.stderr 
print "Hello World!" 

O para redirigir sólo para un determinado print declaración:

print >>sys.stderr, "Hello World!" 

Para restablecer la salida estándar sólo se puede hacer:

sys.stdout = sys.__stdout__ 
+1

Esto puede ser muy confuso cuando luego intente capturar la salida usando la redirección estándar, ¡y descubra que no está capturando nada! p.s. tu __stdout__ está siendo en negrita y esas cosas. – freespace

+1

Una gran precaución acerca de la impresión selectiva en stderr es que esto hace que las líneas aparezcan fuera de lugar, así que a menos que también tenga una marca de tiempo, esto podría ser muy confuso. – haridsv

3

Puede crear un archivo sin búfer y asignarlo a sys.stdou t.

import sys 
myFile= open("a.log", "w", 0) 
sys.stdout= myFile 

No se puede cambiar mágicamente la salida estándar suministrada por el sistema; ya que el sistema operativo lo proporciona a su programa python.

69
# 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 distribución de Python.

+14

Esto ya no funciona en Python 3, ver PEP 3116. – sorin

+0

@sorin: Sí, lo hace. Por favor explica tu reclamo –

+19

En Python 3, la línea anterior arrojará una excepción: 'ValueError: no puede tener E/S de texto sin búfer '. – sorin

4

También puede usar fcntl para cambiar las banderas del archivo en vuelo.

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) 
+1

no es este * nix solamente? –

+0

ah ... eso es probablemente correcto. – jimx

+1

Hay un equivalente de Windows: http://stackoverflow.com/questions/881696/unbuffered-stdout-in-python-as-in-python-u-from-within-the-program/881751#881751 – Tobu

11
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 los viejos sys.stdout, disable_stdout_buffering() no es idempotente, y múltiples llamadas dará lugar a 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) 

(Al añadir a gc.garbage no es un buen ejemplo . idea porque es donde consiguen puestos ciclos unfreeable, y es posible que desee comprobar para aquellos)

+2

Si el viejo 'stdout' aún vive en' sys .__ stdout__' como algunos han sugerido, la basura no será necesaria, ¿no? Aunque es un buen truco. –

4

variante que funciona sin que se caiga (por lo menos en win32; Python 2.7, ipython 0,12), entonces llamado posteriormente (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) 
+0

¿Estás seguro de que esto no está almacenado? – quantum

+1

¿Debe comprobar si 'sys.stdout es sys .__ stdout__' en lugar de confiar en que el objeto de reemplazo tenga un atributo de nombre? – leewz

+0

esto funciona genial si gunicornio no respeta PYTHONUNBUFFERED por alguna razón. –

3

(He publicado un comentario, pero se perdió alguna manera Así que, de nuevo :)

.
  1. Como noté, CPython (al menos en Linux) se comporta de manera diferente dependiendo de a dónde va 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 se almacena en búfer y puede usar las soluciones flush() o la opción -u -u recomendada anteriormente.

  2. ligeramente en relación con el almacenamiento en búfer de salida:
    Si iterar sobre las líneas en la entrada con

    for line in sys.stdin:
    ...

entonces el para aplicación en CPython recogerá 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 salida para cada línea de entrada, esto podría parecerse al almacenamiento en búfer de salida, pero en realidad es de procesamiento por lotes, y por lo tanto, ninguna de las técnicas de flush(), etc. lo ayudará. Curiosamente, no tiene este comportamiento en pypy. Para evitar esto, puede utilizar

while True: line=sys.stdin.readline()
...

+0

[aquí está su comentario] (http://stackoverflow.com/questions/107705/python-output-buffering/107717#comment24604506_107717). Puede ser un error en las versiones anteriores de Python. ¿Podría proporcionar un código de ejemplo? Algo como ['for line in sys.stdin'] (http://ideone.com/TzHwlX) vs. [' para line in iter (sys.stdin.readline, "") '] (http: // ideone. com/mMxn09) – jfs

+0

para la línea en sys.stdin: print ("Línea:" + línea); sys.stdout.flush() – tzp

+0

parece [el error de lectura anticipada] (https://bugs.python.org/issue3907). Solo debería ocurrir en Python 2 y si stdin es un conducto. El código en mi comentario anterior demuestra el problema ('for line in sys.stdin' proporciona una respuesta retrasada) – jfs

7

las siguientes obras 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) 
+0

Ejecuta eso dos veces y se bloquea en Windows :-) –

+0

@MichaelClerx Mmm hmm, recuerda siempre cerrar tus archivos xD. –

4

También puede ejecutar Python con stdbuf utilidad:

stdbuf -oL python <script>

10

Esto se relaciona t o La respuesta de Cristóvão D. Sousa, pero no pude comentar aún.

Una forma recta de avance de la utilización de la flush palabra clave argumento de Python 3 con el fin de siempre tienen salida sin búfer es:

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

después, la impresión siempre enjuagar la salida directa (excepto flush=False es dado).

Tenga en cuenta que (a) esto responde la pregunta solo parcialmente, ya que no redirige todos los resultados. Pero supongo que print es la forma más común de crear resultados en stdout/stderr en python, por lo que estas 2 líneas cubren probablemente la mayoría de los casos de uso.

Nota (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 mezcla con el sys.stdout.

Python 2 no proporciona el argumento flush, pero se puede emular un pitón de tipo 3 función print como se describe aquí https://stackoverflow.com/a/27991478/3734258.

+1

Excepto que no hay kwarg 'flush' en python2. – o11c

+0

@ o11c, sí, tienes razón. Estaba seguro de que lo probé, pero de alguna manera estaba aparentemente confundido (: modifiqué mi respuesta, espero que esté bien ahora. ¡Gracias! – ciem

1

Es posible anular únicawrite método de sys.stdout con uno que llama flush. La implementación del método sugerido está a continuación.

def write_flush(args, w=stdout.write): 
    w(args) 
    stdout.flush() 

valor predeterminado de w argumento mantendrá referencia original write método. Después de definirwrite_flush, se puede anular el write original.

stdout.write = write_flush 

El código se supone que es importado stdout esta manera from sys import stdout.

Cuestiones relacionadas