¿Cómo sobrescribir la impresión anterior en stdout en Python?

113

Si tuviera el siguiente código:

for x in range(10):
     print x

Obtendría la salida de

1
2
etc..

Lo que me gustaría hacer es en lugar de imprimir una nueva línea, quiero reemplazar el valor anterior y sobrescribirlo con el nuevo valor en la misma línea.

ccwhite1
fuente
posible duplicado de Python - Eliminar y reemplazar elementos impresos
Sven Marnach
Como nota al margen, asegúrese de obtener la respuesta que pueda limpiar toda la línea si la línea anterior es más larga que la línea actual.
Fruta

Respuestas:

119

Una forma es utilizar el carácter de retorno de carro ( '\r') para volver al principio de la línea sin avanzar a la siguiente:

for x in range(10):
    print '{0}\r'.format(x),
print

La coma al final de la declaración de impresión le dice que no vaya a la siguiente línea. La última declaración de impresión avanza a la siguiente línea para que su mensaje no sobrescriba su salida final.

Actualizar

Ahora que Python 2 es EOL, una respuesta de Python 3 tiene más sentido. Para Python 3.5 y versiones anteriores:

for x in range(10):
    print('{}\r'.format(x), end="")
print()

En Python 3.6 y versiones posteriores, las cadenas f se leen mejor:

for x in range(10):
    print(f'{x}\r', end="")
print()
Mike DeSimone
fuente
Esto funciona, pero no entiendo un par de partes ... '{0}' y .format
ccwhite1
7
Eso se introdujo en Python 2.6 y (me dijeron) es la forma estándar de formatear cadenas en Python 3; el %operador está en desuso. Básicamente, todas las cadenas ahora tienen un método de formato . En este caso, {0}significa "el primer argumento para format()" (el conteo comienza en 0).
Mike DeSimone
No es una opción si tiene que usar algún software solo para Windows y no tiene las habilidades para ejecutar una máquina virtual.
Mike DeSimone
2
Y si desea tener una terminal cruzada, re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or '')le dará la cadena de borrado hasta el final de la línea correcta para cualquier terminal que tenga el usuario. Recuerde inicializar cursescon algo como curses.setupterm(fd=sys.stdout.fileno()), y utilícelo sys.stdout.isatty()para asegurarse de que su salida no se redirija a un archivo. Consulte code.activestate.com/recipes/475116 para obtener un módulo Python completo con control de cursor y compatibilidad con colores.
Mike DeSimone
1
@PhilMacKay: Lo intenté en Python en mi Mac: En [3]: imprime "Cosa para borrar \ r",; time.sleep (1); print "-------------- \ r", -------------- Creo que tu problema es Windows y sus diferentes extremos de línea. Intente esto: import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));debe imprimir los códigos hexadecimales de la secuencia de caracteres para ir al inicio de la línea.
Mike DeSimone
107

Como terminé aquí a través de Google pero estoy usando Python 3, así es como funcionaría esto en Python 3:

for x in range(10):
    print("Progress {:2.1%}".format(x / 10), end="\r")

Respuesta relacionada aquí: ¿Cómo puedo suprimir la nueva línea después de una declaración impresa?

Pascal
fuente
3
Intenté esto y en su mayoría funciona bien. El único problema es que no está borrando la salida anterior en esa línea, por ejemplo: si su primer bucle imprime testingy su segundo bucle imprime, testla salida después de la segunda pasada seguirá siendo testing- ahora veo que @ Nagasaki45 señaló esto
Eric Uldall
15
en un cuaderno ipython, tengo que hacer:print("\r", message, end="")
grisaitis
1
Esto funciona muy bien, no borra la línea, pero usar suficientes espacios en blanco en la siguiente impresión hace el trabajo (aunque no es elegante). Un comentario que podría agregar es que esto no funciona en la consola de depuración de VS Code, pero funciona en la terminal.
PhilMacKay
31

La respuesta de @Mike DeSimone probablemente funcionará la mayor parte del tiempo. Pero...

for x in ['abc', 1]:
    print '{}\r'.format(x),

-> 1bc

Esto se debe a que '\r'solo vuelve al principio de la línea, pero no borra la salida.

EDITAR: Mejor solución (que mi antigua propuesta a continuación)

Si el soporte POSIX es suficiente para usted, lo siguiente borraría la línea actual y dejaría el cursor al principio:

print '\x1b[2K\r',

Utiliza el código de escape ANSI para borrar la línea de la terminal. Se puede encontrar más información en wikipedia y en esta gran charla .

Respuesta antigua

La solución (no tan buena) que he encontrado se ve así:

last_x = ''
for x in ['abc', 1]:
    print ' ' * len(str(last_x)) + '\r',
    print '{}\r'.format(x),
    last_x = x

-> 1

Una ventaja es que también funcionará en Windows.

Nagasaki45
fuente
1
Hice una función a partir de la 'respuesta anterior' que funciona correctamente (incluso en Windows), vea mi respuesta: stackoverflow.com/a/43952192/965332
erb
25

Tenía la misma pregunta antes de visitar este hilo. Para mí, sys.stdout.write funcionó solo si limpio correctamente el búfer, es decir

for x in range(10):
    sys.stdout.write('\r'+str(x))
    sys.stdout.flush()

Sin vaciar, el resultado se imprime solo al final del script

usuario2793078
fuente
También es una buena idea llenar el resto de la cadena con espacios como stringvar.ljust(10,' ')en el caso de cadenas con longitud variable.
Annarfych
@chris, Don y col. esto es Python 2, la mayoría de las otras respuestas son Python 3. Todos deberían usar Python 3 ahora; Python 2 está al final de su vida útil en 2020.
smci
18

Suprima la nueva línea e imprima \r.

print 1,
print '\r2'

o escriba a stdout:

sys.stdout.write('1')
sys.stdout.write('\r2')
Ignacio Vázquez-Abrams
fuente
9

Prueba esto:

import time
while True:
    print("Hi ", end="\r")
    time.sleep(1)
    print("Bob", end="\r")
    time.sleep(1)

Funcionó para mí. La end="\r"parte hace que sobrescriba la línea anterior.

¡ADVERTENCIA!

Si imprime hi, luego imprime hellousando \r, obtendrá hilloporque la salida escribió sobre las dos letras anteriores. Si imprime hicon espacios (que no aparecen aquí), se mostrará hi. Para solucionar este problema, imprima los espacios usando \r.

Sam Bernstein
fuente
1
Esto no me funciona. Hice frente a ese código, pero mi salida es Hi BobHi BobHi BobHi BobHi Bob. Python 3.7.1
PaulMag
1
Pero la respuesta de Rubixred funcionó. Extraño. A mí me parecen el mismo método. La única diferencia que veo es que \restá al principio en lugar del final.
PaulMag
7

No pude hacer que ninguna de las soluciones en esta página funcionara para IPython , pero una ligera variación en la solución de @ Mike-Desimone hizo el trabajo: en lugar de terminar la línea con el retorno de carro, comience la línea con el retorno de carro:

for x in range(10):
    print '\r{0}'.format(x),

Además, este enfoque no requiere la segunda declaración de impresión.

David Marx
fuente
Incluso eso no funciona en la consola de PyDev, ni tampoco una coma después del paréntesis cerrado de print ().
Noumenon
no funcionó para mí, solo escribe el resultado final (por ejemplo: "Descarga del progreso al 100%") una vez que el proceso está terminado.
Alexandre
2
@Alexandre Funciona bien en IPython o su sucesor Jupyter. Necesita vaciar el búfer. stackoverflow.com/questions/17343688/…
March Ho
7
for x in range(10):
    time.sleep(0.5) # shows how its working
    print("\r {}".format(x), end="")

time.sleep (0.5) es para mostrar cómo se borra la salida anterior y se imprime la nueva salida "\ r" cuando está al inicio del mensaje de impresión, borrará la salida anterior antes de la nueva salida.

Rubixred
fuente
7

Esto funciona en Windows y Python 3.6

import time
for x in range(10):
    time.sleep(0.5)
    print(str(x)+'\r',end='')
Siddhant Sadangi
fuente
5

La respuesta aceptada no es perfecta . La línea que se imprimió primero permanecerá allí y si su segunda impresión no cubre toda la nueva línea, terminará con texto basura.

Para ilustrar el problema, guarde este código como un script y ejecútelo (o simplemente eche un vistazo):

import time

n = 100
for i in range(100):
    for j in range(100):
        print("Progress {:2.1%}".format(j / 100), end="\r")
        time.sleep(0.01)
    print("Progress {:2.1%}".format(i / 100))

La salida se verá así:

Progress 0.0%%
Progress 1.0%%
Progress 2.0%%
Progress 3.0%%

Lo que me funciona es borrar la línea antes de dejar una huella permanente. Siéntase libre de adaptarse a su problema específico:

import time

ERASE_LINE = '\x1b[2K' # erase line command
n = 100
for i in range(100):
    for j in range(100):
        print("Progress {:2.1%}".format(j / 100), end="\r")
        time.sleep(0.01)
    print(ERASE_LINE + "Progress {:2.1%}".format(i / 100)) # clear the line first

Y ahora se imprime como se esperaba:

Progress 0.0%
Progress 1.0%
Progress 2.0%
Progress 3.0%
mimoralea
fuente
5

Aquí hay una versión más limpia, más "plug-and-play", de la respuesta de @ Nagasaki45. A diferencia de muchas otras respuestas aquí, funciona correctamente con cadenas de diferentes longitudes. Lo logra despejando la línea con tantos espacios como la longitud de la última línea impresa. También funcionará en Windows.

def print_statusline(msg: str):
    last_msg_length = len(print_statusline.last_msg) if hasattr(print_statusline, 'last_msg') else 0
    print(' ' * last_msg_length, end='\r')
    print(msg, end='\r')
    sys.stdout.flush()  # Some say they needed this, I didn't.
    print_statusline.last_msg = msg

Uso

Simplemente utilícelo así:

for msg in ["Initializing...", "Initialization successful!"]:
    print_statusline(msg)
    time.sleep(1)

Esta pequeña prueba muestra que las líneas se borran correctamente, incluso para diferentes longitudes:

for i in range(9, 0, -1):
    print_statusline("{}".format(i) * i)
    time.sleep(0.5)
erb
fuente
5
Esta es una gran solución, ya que limpia con elegancia la salida anterior en toda su longitud sin solo imprimir una cantidad arbitraria de espacios en blanco (¡que es a lo que había recurrido anteriormente!) Gracias
Toby Petty
2

Estoy un poco sorprendido de que nadie esté usando el carácter de retroceso. Aquí hay uno que lo usa.

import sys
import time

secs = 1000

while True:
    time.sleep(1)  #wait for a full second to pass before assigning a second
    secs += 1  #acknowledge a second has passed

    sys.stdout.write(str(secs))

    for i in range(len(str(secs))):
        sys.stdout.write('\b')
David Philipe Gil
fuente
asegúrese de descargar después de usar este método o es posible que no se imprima nada en la pantalla. sys.stdout.flush()
Gabriel Fair
2

(Python3) Esto es lo que funcionó para mí. Si solo usa \ 010, dejará caracteres, así que lo modifiqué un poco para asegurarme de que sobrescriba lo que estaba allí. Esto también le permite tener algo antes del primer elemento de impresión y solo elimina la longitud del elemento.

print("Here are some strings: ", end="")
items = ["abcd", "abcdef", "defqrs", "lmnop", "xyz"]
for item in items:
    print(item, end="")
    for i in range(len(item)): # only moving back the length of the item
        print("\010 \010", end="") # the trick!
        time.sleep(0.2) # so you can see what it's doing
RenegadeJr
fuente
2

Una respuesta más basada en las respuestas anteriores.

Contenido de pbar.py: import sys, shutil, datetime

last_line_is_progress_bar=False


def print2(print_string):
    global last_line_is_progress_bar
    if last_line_is_progress_bar:
        _delete_last_line()
        last_line_is_progress_bar=False
    print(print_string)


def _delete_last_line():
    sys.stdout.write('\b\b\r')
    sys.stdout.write(' '*shutil.get_terminal_size((80, 20)).columns)
    sys.stdout.write('\b\r')
    sys.stdout.flush()


def update_progress_bar(current, total):
    global last_line_is_progress_bar
    last_line_is_progress_bar=True

    completed_percentage = round(current / (total / 100))
    current_time=datetime.datetime.now().strftime('%m/%d/%Y-%H:%M:%S')
    overhead_length = len(current_time+str(current))+13
    console_width = shutil.get_terminal_size((80, 20)).columns - overhead_length
    completed_width = round(console_width * completed_percentage / 100)
    not_completed_width = console_width - completed_width
    sys.stdout.write('\b\b\r')

    sys.stdout.write('{}> [{}{}] {} - {}% '.format(current_time, '#'*completed_width, '-'*not_completed_width, current,
                                        completed_percentage),)
    sys.stdout.flush()

Uso de script:

import time
from pbar import update_progress_bar, print2


update_progress_bar(45,200)
time.sleep(1)

update_progress_bar(70,200)
time.sleep(1)

update_progress_bar(100,200)
time.sleep(1)


update_progress_bar(130,200)
time.sleep(1)

print2('some text that will re-place current progress bar')
time.sleep(1)

update_progress_bar(111,200)
time.sleep(1)

print('\n') # without \n next line will be attached to the end of the progress bar
print('built in print function that will push progress bar one line up')
time.sleep(1)

update_progress_bar(111,200)
time.sleep(1)
Krisz
fuente
1

¡Aquí está mi solución! Windows 10, Python 3.7.1

No estoy seguro de por qué funciona este código, pero borra por completo la línea original. Lo compilé a partir de las respuestas anteriores. Las otras respuestas simplemente devolverían la línea al principio, pero si tuviera una línea más corta después, se vería desordenada como se helloconvierte en byelo.

import sys
#include ctypes if you're on Windows
import ctypes
kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
#end ctypes

def clearline(msg):
    CURSOR_UP_ONE = '\033[K'
    ERASE_LINE = '\x1b[2K'
    sys.stdout.write(CURSOR_UP_ONE)
    sys.stdout.write(ERASE_LINE+'\r')
    print(msg, end='\r')

#example
ig_usernames = ['beyonce','selenagomez']
for name in ig_usernames:
    clearline("SCRAPING COMPLETE: "+ name)

Salida: cada línea se reescribirá sin que se muestre ningún texto antiguo:

SCRAPING COMPLETE: selenagomez

Siguiente línea (reescrito completamente en la misma línea):

SCRAPING COMPLETE: beyonce
kposibles
fuente
0

Es mejor sobrescribir toda la línea, de lo contrario, la nueva línea se mezclará con las antiguas si la nueva línea es más corta.

import time, os
for s in ['overwrite!', 'the!', 'whole!', 'line!']:
    print(s.ljust(os.get_terminal_size().columns - 1), end="\r")
    time.sleep(1)

Tuve que usarlo columns - 1en Windows.

Carl Chang
fuente