พิมพ์ในหนึ่งบรรทัดแบบไดนามิก


306

ฉันต้องการสร้างหลายข้อความที่ให้ผลลัพธ์มาตรฐานโดยไม่เห็นบรรทัดใหม่ในระหว่างข้อความ

โดยเฉพาะสมมติว่าฉันมี:

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

ผลลัพธ์คือ:

1
2
3
4
.
.
.

ทำให้สิ่งนี้ดูเป็นอย่างไรแทน:

1 2 3 4 5 ...

ยิ่งไปกว่านั้นเป็นไปได้ไหมที่จะพิมพ์ตัวเลขเดียวผ่านหมายเลขสุดท้ายดังนั้นจึงมีเพียงหนึ่งหมายเลขบนหน้าจอในแต่ละครั้ง?


คำตอบ:


482

เปลี่ยนprint itemเป็น:

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

หากคุณต้องการพิมพ์ข้อมูลใช้ไวยากรณ์ต่อไปนี้แบบไดนามิก:

  • print(item, sep=' ', end='', flush=True) ใน Python 3

จากhttp://docs.python.org/reference/simple_stmts.html#print :> '\n'อักขระจะถูกเขียนที่ส่วนท้ายยกเว้นว่าprintคำสั่งลงท้ายด้วยเครื่องหมายจุลภาค printนี่คือการกระทำเฉพาะในกรณีที่คำสั่งที่มีเพียงคำหลัก
eWall

5
FYI: print (item, end = "") สามารถใช้ได้ใน Python2.7 โดยเพิ่ม 'จากprint_function ในอนาคต ' ที่ด้านบนของไฟล์
Hec

2
แต่flushใช้งานไม่ได้กับfrom __future__ import print_functionโชคไม่ดี
Timmmm

@ewall ถ้าผมจะทำprint(item, end=", ")มันแล้วยังเพิ่ม,ไปยังรายการที่ผ่านมาวิธีการทำป้องกันไม่ให้มันจากการเพิ่ม,ไปยังรายการที่ผ่านมา
MarcoBianchi

@SamirTendulkar หากคุณกำลังใช้สำหรับวงเหมือนคำถามเดิมคุณสามารถทำให้เป็นกรณีพิเศษในการจัดการรายการสุดท้ายในรายการที่แตกต่างกัน ...
eWall

156

โดยวิธี ...... วิธีการรีเฟรชทุกครั้งเพื่อพิมพ์ mi ในที่เดียวเพียงแค่เปลี่ยนจำนวน

โดยทั่วไปวิธีการทำที่อยู่กับรหัสควบคุมขั้ว นี่เป็นกรณีที่ง่ายโดยเฉพาะอย่างยิ่งที่คุณต้องการตัวอักษรพิเศษหนึ่งตัวเท่านั้น: U + 000D CARRIAGE RETURN ซึ่งเขียนด้วย'\r'Python (และภาษาอื่น ๆ อีกมากมาย) นี่คือตัวอย่างที่สมบูรณ์ตามรหัสของคุณ:

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

บางสิ่งเกี่ยวกับสิ่งนี้ที่อาจทำให้ประหลาดใจ:

  • \rไปที่จุดเริ่มต้นของสตริงเพื่อให้ในขณะที่โปรแกรมทำงานเคอร์เซอร์จะเป็นหลังจากที่จำนวน นี่ไม่ใช่แค่เครื่องสำอางค์: ผู้เลียนแบบเครื่องเทอร์มินัลบางคนสับสนมากหากคุณทำอย่างอื่น
  • หากคุณไม่รวมบรรทัดสุดท้ายจากนั้นหลังจากโปรแกรมสิ้นสุดลงเชลล์ของคุณจะพิมพ์พรอมต์ที่ด้านบนของตัวเลข
  • stdout.flushเป็นสิ่งที่จำเป็นในบางระบบหรือคุณจะไม่ได้รับผลใด ๆ ระบบอื่นอาจไม่จำเป็นต้องใช้ แต่ก็ไม่ได้ทำอันตรายใด ๆ

หากคุณพบว่ามันใช้งานไม่ได้สิ่งแรกที่คุณควรสงสัยก็คือเทอร์มินัลอีมูเลเตอร์ของคุณนั้นเป็นรถ vttestโปรแกรมสามารถช่วยให้คุณทดสอบ

คุณสามารถแทนที่stdout.writeด้วยprintคำสั่ง แต่ฉันไม่ต้องการผสมprintกับการใช้โดยตรงของวัตถุไฟล์


ฟังก์ชั่น meen flush () คืออะไร ทำให้มันใช้ได้สำหรับฉันโดยไม่มีฟังก์ชั่นนั้น!
Pol

1
โดยปกติวัตถุไฟล์ของไพ ธ อนรวบรวมข้อมูลเพื่อให้พวกเขาสามารถส่งไปยังระบบปฏิบัติการเป็นกลุ่มก้อนขนาดใหญ่ นี่คือสิ่งที่คุณต้องการเข้าถึงไฟล์บนดิสก์โดยปกติแล้ว แต่มันรบกวนงานประเภทนี้ flush()บังคับให้ส่งไฟล์อะไรก็ตามที่ไปยังระบบปฏิบัติการได้ ฉันประหลาดใจที่มันทำงานให้คุณโดยที่ไม่ต้องทำอะไร - มันไม่ได้ผลสำหรับฉันจนกว่าฉันจะเพิ่มเข้าไป
zwol

1
@ user1995839 มีรหัสควบคุมเพื่อซ่อนเคอร์เซอร์ แต่ถ้าคุณจะทำอย่างนั้นฉันขอแนะนำให้คุณใช้ncursesมัน
zwol

1
sys.stdout.flush()ดูเหมือนจะเป็นคำสาปแช่งของฉันFreebsdด้วยPython 2.7
Hakim

1
โปรดทราบว่าการคืนค่าขนส่งไม่ทำงานอย่างถูกต้องภายใน Python REPL (อย่างน้อยสำหรับฉันใน Ubuntu); คุณจำเป็นต้องเรียกใช้งานสคริปต์ไม่ใช่เพียงแค่เรียกใช้คำสั่ง REPL เพื่อให้สิ่งนี้สามารถทำงานได้
Mark Amery

50

ใช้print item,เพื่อทำให้คำสั่งพิมพ์ละเว้นการขึ้นบรรทัดใหม่

ในหลาม 3 print(item, end=" ")ก็

หากคุณต้องการให้ทุกหมายเลขแสดงในที่เดียวกันให้ใช้ตัวอย่าง (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),

ใน Python 3 มันซับซ้อนกว่าเล็กน้อย ที่นี่คุณต้องล้างข้อมูลsys.stdoutหรือไม่พิมพ์อะไรเลยจนกว่าจะเสร็จสิ้นการวนซ้ำ:

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

ที่จริงแล้วฉันคิดว่าโซลูชันของคุณจะพิมพ์ backspaces จำนวนเท่าเดิมทุกครั้ง ดังนั้นถ้าเป็น 20 มันจะพิมพ์ backspace พิเศษในขณะที่พิมพ์ตัวเลข 1..9 คุณไม่ควรคำนวณตัวเลขภายในลูปและยึดมันจากค่าของ i หรือ?
Adam Crossland

มันจะจัดชิดหมายเลขและลบทุกอย่าง ฉันยังไม่ได้ทดสอบว่ามันเร็วกว่าในการทำเช่นนั้นหรือเพื่อคำนวณในแต่ละการวนซ้ำจำนวนหลักที่จะถูกลบในตอนนี้
ทิม Pietzcker

หรือยึดจากค่าก่อนหน้าของ i เป็นบัญชีเมื่อจำนวนตัวเลขเปลี่ยนแปลง
Adam Crossland

อาใช่ฉันได้ดูเหตุผลที่ถูกต้องมากเกินไป ทางออกที่ดี
Adam Crossland

ฟังดูค่อนข้างซับซ้อน ฉันอาจจะไม่เข้าใจสิ่งที่ฉันทำที่นั่นถ้าฉันต้องดูรหัสของฉันจากปีนี้ (เพิ่งมีการอ่านโปรแกรมที่ผมเขียนในปี 2007 - ฉันจึงดีใจที่ฉันเขียนมันในหลาม.)
ทิม Pietzcker

16

เช่นเดียวกับตัวอย่างอื่น ๆ
ฉันใช้วิธีการที่คล้ายกัน แต่แทนที่จะใช้เวลาในการคำนวณความยาวผลลัพธ์ล่าสุด ฯลฯ

ฉันเพียงแค่ใช้ ANSI code escapes เพื่อย้ายกลับไปที่จุดเริ่มต้นของบรรทัดแล้วล้างข้อมูลบรรทัดนั้นทั้งหมดก่อนพิมพ์ผลลัพธ์สถานะปัจจุบันของฉัน

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

หากต้องการใช้ในลูปการวนซ้ำคุณจะเรียกสิ่งที่ชอบ

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

ดูเพิ่มเติมที่นี่


14

คุณสามารถเพิ่มเครื่องหมายจุลภาคต่อท้ายคำสั่งการพิมพ์ของคุณเพื่อพิมพ์ช่องว่างแทนการขึ้นบรรทัดใหม่ในแต่ละรอบ

print item,

หรือหากคุณใช้ Python 2.6 หรือใหม่กว่าคุณสามารถใช้ฟังก์ชั่นการพิมพ์ใหม่ซึ่งจะช่วยให้คุณระบุได้ว่าไม่มีช่องว่างที่ควรจะอยู่ที่ส่วนท้ายของแต่ละรายการที่พิมพ์ (หรืออนุญาตให้คุณระบุสิ่งที่คุณต้องการ ต้องการ):

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

ในที่สุดคุณสามารถเขียนโดยตรงไปยังเอาต์พุตมาตรฐานโดยนำเข้าจากโมดูล sys ซึ่งส่งคืนวัตถุเหมือนไฟล์:

from sys import stdout
...
stdout.write( str(item) )

13

เปลี่ยนแปลง

print item

ถึง

print "\033[K", item, "\r",
sys.stdout.flush()
  • "\ 033 [K" จะถูกล้างจนสุดบรรทัด
  • the \ r กลับไปที่จุดเริ่มต้นของบรรทัด
  • คำสั่ง flush ทำให้แน่ใจว่าจะแสดงขึ้นทันทีเพื่อให้คุณได้รับเอาต์พุตแบบเรียลไทม์

สิ่งนี้ดีมากสำหรับ Python 2.7 คุณสามารถให้การอ้างอิงถึงตำแหน่งที่คุณเรียนรู้เกี่ยวกับ\033[Kรหัสได้หรือไม่? ฉันต้องการเรียนรู้เพิ่มเติมเกี่ยวกับพวกเขา
Klik

printภายในวูบวาบแล้ว ไม่จำเป็นต้องโทรหา printแตกต่างจากsys.stdout.write()ที่คุณต้องล้างหน้าจอ
Gabriel Fair

5

ฉันคิดว่าการเข้าร่วมแบบง่ายควรใช้งานได้:

nl = []
for x in range(1,10):nl.append(str(x))
print ' '.join(nl)

4
print ' '.join(str(x) for x in range(1, 10))ความเข้าใจจะอ่านที่ดีขึ้นและอาจจะเร็วขึ้น:
หมู่ใจ

ฉันสองใช้รายการความเข้าใจ ในกรณีนี้มันจะทำให้อ่านง่ายขึ้น
Austin Henley

5

คำตอบอีกข้อหนึ่งที่ฉันใช้ใน 2.7 คือฉันกำลังพิมพ์ "" ทุกครั้งที่มีการวนซ้ำ (เพื่อบ่งบอกผู้ใช้ว่าสิ่งต่าง ๆ ยังคงทำงานอยู่) คือ:

print "\b.",

มันพิมพ์ "." อักขระที่ไม่มีช่องว่างระหว่างกัน มันดูดีขึ้นเล็กน้อยและใช้งานได้ดี \ b เป็นอักขระถอยกลับสำหรับผู้ที่สงสัย


4

คำตอบที่ซับซ้อนมากมาย หากคุณมี python 3 ให้ใส่\rตอนเริ่มต้นของการพิมพ์และเพิ่มลงend='', flush=Trueไป:

import time

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

นี้จะเขียน0 foo barแล้ว1 foo barฯลฯ ในสถานที่


ขอบคุณนั่นเป็นเพียงสิ่งที่ฉันต้องการprint('\r' + filename + " ", end = '', flush=True)
McPeppr

3

หากต้องการให้หมายเลขเขียนทับกันคุณสามารถทำสิ่งนี้:

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

ที่ควรทำงานตราบใดที่พิมพ์ตัวเลขในคอลัมน์แรก

แก้ไข: นี่คือรุ่นที่จะใช้งานได้แม้ว่าจะไม่ได้พิมพ์ในคอลัมน์แรก

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

ฉันควรทราบว่ารหัสนี้ผ่านการทดสอบและใช้งานได้ดีใน Python 2.5 บน Windows ในคอนโซล WIndows บางคนอาจต้องล้างข้อมูล stdout เพื่อดูผลลัพธ์ YMMV


3

"โดยวิธี ...... วิธีการรีเฟรชทุกครั้งเพื่อพิมพ์ mi ในที่เดียวเพียงเปลี่ยนหมายเลข"

มันเป็นเรื่องที่ยุ่งยากมาก สิ่งที่แนะนำให้zack (รหัสเอาต์พุตคอนโซลควบคุม) เป็นวิธีหนึ่งในการบรรลุเป้าหมายดังกล่าว

คุณสามารถใช้ (n) curses ได้ แต่ส่วนใหญ่จะทำงานบน * nixes

บน Windows (และต่อไปจะเป็นส่วนที่น่าสนใจ) ซึ่งไม่ค่อยมีใครพูดถึง (ฉันไม่เข้าใจว่าทำไม) คุณสามารถใช้การเชื่อมโยง Python กับ WinAPI ( http://sourceforge.net/projects/pywin32/ด้วย ActivePython ตามค่าเริ่มต้น) - ไม่ใช่ มันยากและใช้งานได้ดี นี่คือตัวอย่างเล็ก ๆ :

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 )

หรือถ้าคุณต้องการใช้print(คำสั่งหรือฟังก์ชั่นไม่แตกต่างกัน):

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 โมดูลช่วยให้คุณทำสิ่งที่น่าสนใจมากมายกับ windows console ... ฉันไม่ใช่แฟนตัวยงของ WinAPI แต่เมื่อเร็ว ๆ นี้ฉันรู้ว่าอย่างน้อยครึ่งหนึ่งของความเกลียดชังของฉันที่มีต่อมันเกิดจากการเขียนรหัส WinAPI ใน C - pythonic ใช้งานง่ายกว่ามาก

คำตอบอื่น ๆ ที่ยอดเยี่ยมและไพเราะแน่นอน แต่ ... ถ้าฉันต้องการพิมพ์ในบรรทัดก่อนหน้า ? หรือเขียนข้อความหลายบรรทัดให้ชัดเจนกว่าและเขียนบรรทัดเดิมอีกครั้ง? โซลูชันของฉันทำให้เป็นไปได้



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.

0

หากคุณต้องการพิมพ์ตัวเลขคุณสามารถหลีกเลี่ยงการวนซ้ำ

# 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 97 97 99 100 ใช้เวลา 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 97 97 99 100 เอา 491.466823551 มิลลิวินาที


mapควรจะได้เร็วยิ่งขึ้นกับรายการความเข้าใจแทน
cji

ใช่เป็นตัวอย่างที่ดี ฉันชอบ แต่ฉันต้องแสดงผลลัพธ์ของการแยกวิเคราะห์ ครั้งแรกที่ฉันนับรายการฐานข้อมูลที่ฉันกว่าพิมพ์เหลืออยู่เท่าไหร่
Pol

0

วิธีที่ดีที่สุดในการบรรลุเป้าหมายนี้คือการใช้\rตัวละคร

ลองใช้รหัสด้านล่าง:

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

0

ใน Python 3 คุณสามารถทำสิ่งนี้ได้:

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

ขาออก:

1 2 3 4 5 6 7 8 9 

Tuple:คุณสามารถทำสิ่งเดียวกันได้ด้วย tuple:

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

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

ขาออก:

1 - 2 - 3 - 4 - 5 - 

ตัวอย่างอื่น:

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

ขาออก:

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

คุณสามารถแกะ tuple ของคุณแบบนี้ได้:

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)   

ขาออก:

1 2
A B
3 4
Cat Dog

รูปแบบอื่น:

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

ขาออก:

1
2


A
B


3
4


Cat
Dog

ฉันลองตัวอย่างทั้งหมดที่นี่และไม่มีการทำงานใน python 3.8 โปรดรับคำตอบเพิ่มเติม
KOTZ

ฉันได้ทดสอบด้วย python 3.7
Stryker

สิ่งที่ฉันหมายถึงไม่มีตัวเลือกเหล่านี้เขียนทับหมายเลขสุดท้ายเหมือนที่แนะนำในตอนท้ายของคำถาม
KOTZ

0

เครื่องหมายจุลภาคที่ท้ายคำสั่งการพิมพ์ละเว้นบรรทัดใหม่

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

แต่นี่ไม่ได้เขียนทับ


0

สำหรับผู้ที่ดิ้นรนอย่างที่ฉันทำฉันพบสิ่งต่อไปนี้ซึ่งดูเหมือนว่าทำงานได้ทั้งใน python 3.7.4 และ 3.5.2

ฉันขยายช่วงจาก 100 เป็น 1,000,000 เพราะมันทำงานเร็วมากและคุณอาจไม่เห็นผลลัพธ์ นี่เป็นเพราะผลข้างเคียงของการตั้งค่าend='\r'คือการวนซ้ำสุดท้ายจะล้างเอาต์พุตทั้งหมด จำเป็นต้องใช้หมายเลขที่ยาวขึ้นเพื่อแสดงว่าใช้ได้ ผลลัพธ์นี้อาจไม่เป็นที่ต้องการในทุกกรณี แต่ใช้ได้ในเหมืองและ OP ไม่ได้ระบุวิธีใดวิธีหนึ่ง คุณสามารถหลีกเลี่ยงสิ่งนี้โดยใช้คำสั่ง if ที่ประเมินความยาวของอาร์เรย์ที่วนซ้ำเป็นต้นกุญแจสำคัญในการทำให้มันทำงานในกรณีของฉันคือการจับคู่วงเล็บ"{}"ด้วย.format()ด้วยมิฉะนั้นจะใช้งานไม่ได้

ด้านล่างควรทำงานตามที่เป็นอยู่:

#!/usr/bin/env python3

for item in range(1,1000000):
    print("{}".format(item), end='\r', flush=True)

0
for item in range(1,100):
    if item==99:
        print(item,end='')
    else:
        print (item,end=',')

เอาท์พุท: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,17,18,18,19,20,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


0

หรือง่ายกว่า:

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

end="\r" จะเขียนทับตั้งแต่ต้น [0:] ของงานพิมพ์ครั้งแรก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.