Impression dynamique sur une seule ligne

307

Je voudrais faire plusieurs déclarations qui donnent une sortie standard sans voir de nouvelles lignes entre les déclarations.

Plus précisément, supposons que j'ai:

for item in range(1,100):
    print item

Le résultat est:

1
2
3
4
.
.
.

Comment obtenir ceci à la place:

1 2 3 4 5 ...

Encore mieux, est-il possible d'imprimer le numéro unique sur le dernier numéro, donc un seul numéro est à l'écran à la fois?

Pol
la source

Réponses:

483

Changer print itempour:

  • print item, en Python 2.7
  • print(item, end=" ") en Python 3

Si vous souhaitez imprimer les données dynamiquement, utilisez la syntaxe suivante:

  • print(item, sep=' ', end='', flush=True) en Python 3
ewall
la source
Depuis http://docs.python.org/reference/simple_stmts.html#print :> Un '\n'caractère est écrit à la fin, sauf si l' printinstruction se termine par une virgule. Il s'agit de la seule action si l'instruction contient uniquement le mot clé print.
ewall
5
Pour info: print (item, end = "") peut être utilisé en Python2.7 en ajoutant 'from future import print_function' en haut du fichier.
Hec
2
Mais flushne fonctionne pas avec from __future__ import print_functionmalheureusement.
Timmmm
@ewall si je devais le faire print(item, end=", ")ajoute ,ensuite au dernier élément comment faire pour l'empêcher d'ajouter ,au dernier élément
MarcoBianchi
@SamirTendulkar Si vous utilisez une boucle for comme la question d'origine, vous pouvez créer un cas spécial pour gérer le dernier élément de la liste différemment ...
ewall
156

Au fait ...... Comment le rafraîchir à chaque fois pour qu'il s'imprime en un seul endroit, changez simplement le numéro.

En général, la façon de le faire est d'utiliser des codes de contrôle de terminal . Il s'agit d'un cas particulièrement simple, pour lequel vous n'avez besoin que d'un caractère spécial: U + 000D CARRIAGE RETURN, qui est écrit '\r'en Python (et dans de nombreux autres langages). Voici un exemple complet basé sur votre code:

from sys import stdout
from time import sleep
for i in range(1,20):
    stdout.write("\r%d" % i)
    stdout.flush()
    sleep(1)
stdout.write("\n") # move the cursor to the next line

Certaines choses à ce sujet peuvent être surprenantes:

  • Le \rva au début de la chaîne de sorte que, pendant l'exécution du programme, le curseur sera toujours après le numéro. Ce n'est pas seulement cosmétique: certains émulateurs de terminaux sont très confus si vous le faites dans l'autre sens.
  • Si vous n'incluez pas la dernière ligne, après la fin du programme, votre shell affichera son invite au-dessus du numéro.
  • Le stdout.flushest nécessaire sur certains systèmes, ou vous n'obtiendrez aucune sortie. D'autres systèmes peuvent ne pas l'exiger, mais cela ne fait aucun mal.

Si vous trouvez que cela ne fonctionne pas, la première chose que vous devez soupçonner est que votre émulateur de terminal est bogué. Le programme vttest peut vous aider à le tester.

Vous pouvez remplacer le stdout.writepar une printdéclaration mais je préfère ne pas mélanger printavec l'utilisation directe des objets fichier.

zwol
la source
Quelle fonction meen flush ()? Parce que ça marche pour moi sans cette fonction!
Pol
1
Normalement, les objets fichier de Python accumulent des données afin qu'ils puissent les envoyer au système d'exploitation en gros morceaux. C'est ce que vous voulez généralement pour accéder aux fichiers sur disque, mais cela interfère avec ce type de tâche. flush()force un fichier à envoyer tout ce qu'il a au système d'exploitation en ce moment. Je suis surpris que cela fonctionne pour vous sans cela - cela n'a pas fonctionné pour moi jusqu'à ce que je l'ajoute.
zwol
1
@ user1995839 Il existe des codes de contrôle pour masquer le curseur, mais si vous allez le faire, je vous recommande fortement de l'utiliser ncursespour le faire.
zwol
1
sys.stdout.flush()semble être mendatory sur mon FreebsdavecPython 2.7
Hakim
1
Notez que le retour chariot ne fonctionne pas correctement dans Python REPL (au moins pour moi, sur Ubuntu); vous devez exécuter un script , pas seulement exécuter une commande REPL, pour que tout cela fonctionne.
Mark Amery
50

Utilisez print item,pour que l'instruction print omet la nouvelle ligne.

En Python 3, c'est print(item, end=" ").

Si vous voulez que chaque nombre s'affiche au même endroit, utilisez par exemple (Python 2.7):

to = 20
digits = len(str(to - 1))
delete = "\b" * (digits + 1)
for i in range(to):
    print "{0}{1:{2}}".format(delete, i, digits),

En Python 3, c'est un peu plus compliqué; ici, vous devez vider sys.stdoutou il n'imprimera rien avant la fin de la boucle:

import sys
to = 20
digits = len(str(to - 1))
delete = "\b" * (digits)
for i in range(to):
   print("{0}{1:{2}}".format(delete, i, digits), end="")
   sys.stdout.flush()
Tim Pietzcker
la source
En fait, il me semble que votre solution imprimera à chaque fois le même nombre d'espaces arrière. Ainsi, si to vaut 20, il imprimera un retour arrière supplémentaire lors de l'impression des chiffres 1 à 9. Ne devriez-vous pas calculer des chiffres à l'intérieur de la boucle et la baser sur la valeur de i?
Adam Crossland
Il justifie à droite le nombre et supprime toujours tout. Je n'ai pas testé s'il est plus rapide de le faire ou de calculer à chaque itération le nombre de chiffres à effacer à l'instant.
Tim Pietzcker
Ou, basez-le sur la valeur précédente de i pour tenir compte lorsque le nombre de chiffres change.
Adam Crossland
Ah, oui, j'avais négligé la bonne justification. Bonne solution.
Adam Crossland
Cela semble assez compliqué. Je ne comprendrais probablement pas ce que j'avais fait là-bas si je devais consulter mon code dans un an. (Je viens de lire un programme que j'ai écrit en 2007 - je suis tellement content de l'avoir écrit en Python.)
Tim Pietzcker
16

Comme les autres exemples,
j'utilise une approche similaire, mais au lieu de passer du temps à calculer la dernière longueur de sortie, etc.,

J'utilise simplement les échappements de code ANSI pour revenir au début de la ligne, puis effacer cette ligne entière avant d'imprimer ma sortie d'état actuelle.

import sys

class Printer():
    """Print things to stdout on one line dynamically"""
    def __init__(self,data):
        sys.stdout.write("\r\x1b[K"+data.__str__())
        sys.stdout.flush()

Pour l'utiliser dans votre boucle d'itération, vous appelleriez simplement quelque chose comme:

x = 1
for f in fileList:
    ProcessFile(f)
    output = "File number %d completed." % x
    Printer(output)
    x += 1   

Voir plus ici

Matthieu
la source
14

Vous pouvez ajouter une virgule de fin à votre instruction d'impression pour imprimer un espace au lieu d'une nouvelle ligne à chaque itération:

print item,

Alternativement, si vous utilisez Python 2.6 ou version ultérieure, vous pouvez utiliser la nouvelle fonction d'impression, qui vous permettrait de spécifier que même pas d'espace ne devrait venir à la fin de chaque élément en cours d'impression (ou vous permet de spécifier quelle extrémité vous vouloir):

from __future__ import print_function
...
print(item, end="")

Enfin, vous pouvez écrire directement sur la sortie standard en l'important à partir du module sys, qui renvoie un objet de type fichier:

from sys import stdout
...
stdout.write( str(item) )
Eli Courtwright
la source
13

changement

print item

à

print "\033[K", item, "\r",
sys.stdout.flush()
  • "\ 033 [K" efface jusqu'à la fin de la ligne
  • le \ r, retourne au début de la ligne
  • l'instruction flush s'assure qu'elle s'affiche immédiatement afin que vous obteniez une sortie en temps réel.
Brooks
la source
C'est génial pour Python 2.7. Pouvez-vous indiquer où vous avez découvert le \033[Kcode? J'aimerais en savoir plus à leur sujet.
Klik
printrince déjà en interne. Pas besoin de l'appeler. printest différent de l' sys.stdout.write()endroit où vous devez vider l'écran
Gabriel Fair
5

Je pense qu'une simple jointure devrait fonctionner:

nl = []
for x in range(1,10):nl.append(str(x))
print ' '.join(nl)
xortion
la source
4
Une compréhension serait mieux lire et probablement plus rapide: print ' '.join(str(x) for x in range(1, 10)).
Mu Mind
J'appuie l'utilisation d'une liste de compréhension. Dans ce cas, il serait même plus facile à lire.
Austin Henley
5

Une autre réponse que j'utilise sur 2.7 où j'imprime juste un "." chaque fois qu'une boucle s'exécute (pour indiquer à l'utilisateur que les choses fonctionnent toujours) est la suivante:

print "\b.",

Il imprime le "." caractères sans espaces entre chacun. Il a l'air un peu mieux et fonctionne plutôt bien. Le \ b est un caractère de retour arrière pour ceux qui se demandent.

copeland3300
la source
4

Tant de réponses compliquées. Si vous avez python 3, mettez simplement \rau début de l'impression, et ajoutez- end='', flush=Truey:

import time

for i in range(10):
    print(f'\r{i} foo bar', end='', flush=True)
    time.sleep(0.5)

Cela écrira 0 foo bar, puis 1 foo baretc, sur place.

Hugh Perkins
la source
Merci, c'était exactement ce dont j'avais besoinprint('\r' + filename + " ", end = '', flush=True)
McPeppr
3

Pour que les nombres s’écrasent, vous pouvez faire quelque chose comme ceci:

for i in range(1,100):
    print "\r",i,

Cela devrait fonctionner tant que le numéro est imprimé dans la première colonne.

EDIT: Voici une version qui fonctionnera même si elle n'est pas imprimée dans la première colonne.

prev_digits = -1
for i in range(0,1000):
    print("%s%d" % ("\b"*(prev_digits + 1), i)),
    prev_digits = len(str(i))

Je dois noter que ce code a été testé et fonctionne très bien en Python 2.5 sur Windows, dans la console WIndows. Selon certains autres, un rinçage de la sortie standard peut être nécessaire pour voir les résultats. YMMV.

Adam Crossland
la source
3

"Au fait ... Comment le rafraîchir à chaque fois pour qu'il s'imprime en un seul endroit, il suffit de changer le numéro."

C'est un sujet vraiment délicat. Quel zack suggéré (produire des codes de contrôle de console) est un moyen d'y parvenir.

Vous pouvez utiliser (n) curses, mais cela fonctionne principalement sur * nixes.

Sur Windows (et voici une partie intéressante) qui est rarement mentionné (je ne comprends pas pourquoi), vous pouvez utiliser des liaisons Python vers WinAPI ( http://sourceforge.net/projects/pywin32/ également avec ActivePython par défaut) - ce n'est pas que dur et fonctionne bien. Voici un petit exemple:

import win32console, time

output_handle = win32console.GetStdHandle(  win32console.STD_OUTPUT_HANDLE )
info = output_handle.GetConsoleScreenBufferInfo()
pos = info["CursorPosition"]

for i in "\\|/-\\|/-":
    output_handle.WriteConsoleOutputCharacter( i, pos )
    time.sleep( 1 )

Ou, si vous souhaitez utiliser print(instruction ou fonction, aucune différence):

import win32console, time

output_handle = win32console.GetStdHandle(  win32console.STD_OUTPUT_HANDLE )
info = output_handle.GetConsoleScreenBufferInfo()
pos = info["CursorPosition"]

for i in "\\|/-\\|/-":
    print i
    output_handle.SetConsoleCursorPosition( pos )
    time.sleep( 1 )

win32console Le module vous permet de faire beaucoup plus de choses intéressantes avec la console Windows ... Je ne suis pas un grand fan de WinAPI, mais récemment j'ai réalisé qu'au moins la moitié de mon antipathie à son égard était causée par l'écriture de code WinAPI en C - les liaisons pythoniques sont beaucoup plus facile à utiliser.

Toutes les autres réponses sont excellentes et pythoniques, bien sûr, mais ... Et si je voulais imprimer sur la ligne précédente ? Ou écrire du texte multiligne, puis l'effacer et réécrire les mêmes lignes? Ma solution rend cela possible.

cji
la source
2

pour Python 2.7

for x in range(0, 3):
    print x,

pour Python 3

for x in range(0, 3):
    print(x, end=" ")
Adnan Ghaffar
la source
1
In [9]: print?
Type:           builtin_function_or_method
Base Class:     <type 'builtin_function_or_method'>
String Form:    <built-in function print>
Namespace:      Python builtin
Docstring:
    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.
gtrak
la source
0

Si vous souhaitez simplement imprimer les chiffres, vous pouvez éviter la boucle.

# python 3
import time

startnumber = 1
endnumber = 100

# solution A without a for loop
start_time = time.clock()
m = map(str, range(startnumber, endnumber + 1))
print(' '.join(m))
end_time = time.clock()
timetaken = (end_time - start_time) * 1000
print('took {0}ms\n'.format(timetaken))

# solution B: with a for loop
start_time = time.clock()
for i in range(startnumber, endnumber + 1):
    print(i, end=' ')
end_time = time.clock()
timetaken = (end_time - start_time) * 1000
print('\ntook {0}ms\n'.format(timetaken))

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100 a pris 21.1986929975ms

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100 a pris 491.466823551ms

Mahmoud Kassem
la source
Devrait être encore plus rapide avec la compréhension de la liste au lieu de map.
cji
Oui de bons exemples. J'aime ça, mais je dois montrer le résultat de l'analyse. Je compte d'abord les éléments de la base de données que j'imprime combien restent.
Pol
0

La meilleure façon d'y parvenir est d'utiliser le \rpersonnage

Essayez simplement le code ci-dessous:

import time
for n in range(500):
  print(n, end='\r')
  time.sleep(0.01)
print()  # start new line so most recently printed number stays
vitiral
la source
0

En Python 3, vous pouvez le faire de cette façon:

for item in range(1,10):
    print(item, end =" ")

Les sorties:

1 2 3 4 5 6 7 8 9 

Tuple: Vous pouvez faire la même chose avec un tuple:

tup = (1,2,3,4,5)

for n in tup:
    print(n, end = " - ")

Les sorties:

1 - 2 - 3 - 4 - 5 - 

Un autre exemple:

list_of_tuples = [(1,2),('A','B'), (3,4), ('Cat', 'Dog')]
for item in list_of_tuples:
    print(item)

Les sorties:

(1, 2)
('A', 'B')
(3, 4)
('Cat', 'Dog')

Vous pouvez même déballer votre tuple comme ceci:

list_of_tuples = [(1,2),('A','B'), (3,4), ('Cat', 'Dog')]

# Tuple unpacking so that you can deal with elements inside of the tuple individually
for (item1, item2) in list_of_tuples:
    print(item1, item2)   

Les sorties:

1 2
A B
3 4
Cat Dog

une autre variante:

list_of_tuples = [(1,2),('A','B'), (3,4), ('Cat', 'Dog')]
for (item1, item2) in list_of_tuples:
    print(item1)
    print(item2)
    print('\n')

Les sorties:

1
2


A
B


3
4


Cat
Dog
Stryker
la source
J'ai essayé tous les exemples ici et aucun ne fonctionne en python 3.8 Veuillez obtenir plus de réponses.
KOTZ
Je l'ai testé avec python 3.7
Stryker
Ce que je veux dire, c'est qu'aucune de ces options n'écrase le dernier chiffre comme il a été suggéré à la fin de la question.
KOTZ
0

Une virgule à la fin de l'instruction print omet la nouvelle ligne.

for i in xrange(1,100):
  print i,

mais cela ne remplace pas.

éruciforme
la source
0

Pour ceux qui ont du mal comme moi, j'ai trouvé ce qui suit qui semble fonctionner à la fois en python 3.7.4 et 3.5.2.

J'ai étendu la plage de 100 à 1 000 000 car il fonctionne très rapidement et vous ne pouvez pas voir la sortie. En effet, l'un des effets secondaires du paramètre end='\r'est que l'itération de boucle finale efface toute la sortie. Un nombre plus long était nécessaire pour démontrer que cela fonctionne. Ce résultat n'est peut-être pas souhaitable dans tous les cas, mais était bien dans le mien, et OP n'a pas spécifié d'une manière ou d'une autre. Vous pouvez potentiellement contourner cela avec une instruction if qui évalue la longueur du tableau en cours d'itération, etc. La clé pour le faire fonctionner dans mon cas était de coupler les crochets "{}"avec .format(). Sinon, cela n'a pas fonctionné.

Ci-dessous devrait fonctionner tel quel:

#!/usr/bin/env python3

for item in range(1,1000000):
    print("{}".format(item), end='\r', flush=True)
Structure
la source
0
for item in range(1,100):
    if item==99:
        print(item,end='')
    else:
        print (item,end=',')

Sortie: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49, 50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74, 75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99

Alex
la source
0

Ou encore plus simple:

import time
a = 0
while True:
    print (a, end="\r")
    a += 1
    time.sleep(0.1)

end="\r" remplacera depuis le début [0:] de la première impression.

Evan Schwartzentruber
la source