วิธีการล้างเอาท์พุทของฟังก์ชั่นการพิมพ์?


1216

ฉันจะบังคับให้ฟังก์ชั่นการพิมพ์ของ Python ส่งออกไปยังหน้าจอได้อย่างไร

นี่ไม่ใช่สิ่งที่ซ้ำกันของปิดใช้งานการบัฟเฟอร์เอาต์พุต - คำถามที่เชื่อมโยงกำลังพยายามเอาท์พุทที่ไม่มีบัฟเฟอร์ในขณะที่มันเป็นเรื่องทั่วไป คำตอบยอดนิยมในคำถามนั้นมีพลังมากเกินไปหรือเกี่ยวข้องกับคำถามนี้ (ไม่ใช่คำตอบที่ดีสำหรับเรื่องนี้) และเป็นคำถามที่พบได้ใน Google โดยมือใหม่ที่เป็นญาติ

คำตอบ:


1428

บน Python 3 printสามารถรับflushอาร์กิวเมนต์ตัวเลือกได้

print("Hello world!", flush=True)

ใน Python 2 คุณต้องทำ

import sys
sys.stdout.flush()

printหลังจากเรียก ตามค่าเริ่มต้นให้printพิมพ์sys.stdout(ดูเอกสารประกอบสำหรับข้อมูลเพิ่มเติมเกี่ยวกับวัตถุไฟล์ )


346

วิ่งpython -hฉันเห็นตัวเลือกบรรทัดคำสั่ง :

-u: stdout ไบนารีที่ไม่มีบัฟเฟอร์และ stderr PYTHONUNBUFFERED = x ดูหน้า man สำหรับรายละเอียดเกี่ยวกับการบัฟเฟอร์ภายในที่เกี่ยวข้องกับ '-u'

นี่คือเอกสารที่เกี่ยวข้อง


320

ตั้งแต่ Python 3.3 คุณสามารถบังคับปกติprint()ฟังก์ชั่นการล้างโดยไม่จำเป็นต้องใช้sys.stdout.flush(); เพียงตั้งค่าอาร์กิวเมนต์คำหลัก "flush" เป็นจริง จากเอกสาร :

พิมพ์ (* วัตถุ, sep = '', end = '\ n', ไฟล์ = sys.stdout, flush = False)

พิมพ์วัตถุไปยังไฟล์สตรีมคั่นด้วย sep และตามด้วยสิ้นสุด ต้องระบุ sep, end และ file เป็นข้อโต้แย้งคำหลัก

อาร์กิวเมนต์ที่ไม่ใช่คำหลักทั้งหมดจะถูกแปลงเป็นสตริงเช่น str () ทำและเขียนลงในสตรีมคั่นด้วย sep และตามด้วยสิ้นสุด ทั้ง sep และ end ต้องเป็นสตริง พวกเขาสามารถเป็น None ซึ่งหมายถึงการใช้ค่าเริ่มต้น หากไม่ได้รับวัตถุให้พิมพ์ () เพียงแค่เขียนจบ

อาร์กิวเมนต์ของไฟล์ต้องเป็นวัตถุที่มีเมธอด write (string) หากไม่มีอยู่หรือไม่มีจะใช้ sys.stdout ไม่ว่าเอาต์พุตจะถูกบัฟเฟอร์มักจะถูกกำหนดโดยไฟล์ แต่ถ้าอาร์กิวเมนต์คีย์เวิร์ด flush เป็นจริงสตรีมจะถูกฟลัชต์แบบบังคับ


198

วิธีการล้างผลลัพธ์ของการพิมพ์ Python

ฉันแนะนำห้าวิธีในการทำสิ่งนี้:

  • ใน Python 3 การโทรprint(..., flush=True)(อาร์กิวเมนต์ flush ไม่มีในฟังก์ชั่นการพิมพ์ของ Python 2 และไม่มีคำสั่งอะนาล็อกสำหรับคำสั่งการพิมพ์)
  • โทรหาfile.flush()ไฟล์ที่ส่งออก (เราสามารถตัดฟังก์ชั่นการพิมพ์ของ python 2 เพื่อทำสิ่งนี้) ตัวอย่างเช่นsys.stdout
  • ใช้สิ่งนี้กับการเรียกใช้ฟังก์ชั่นการพิมพ์ทุกครั้งในโมดูลที่มีฟังก์ชั่นบางส่วน
    print = partial(print, flush=True)นำไปใช้กับโมดูลทั่วโลก
  • ใช้สิ่งนี้กับกระบวนการที่มีการตั้งค่าสถานะ ( -u) ที่ส่งผ่านไปยังคำสั่งตัวแปล
  • ใช้สิ่งนี้กับทุก ๆ ไพ ธ อนกระบวนการในสภาพแวดล้อมของคุณด้วยPYTHONUNBUFFERED=TRUE(และยกเลิกการตั้งค่าตัวแปรเพื่อยกเลิกสิ่งนี้)

Python 3.3+

การใช้ Python 3.3 หรือสูงกว่าคุณสามารถกำหนดflush=Trueเป็นอาร์กิวเมนต์คำหลักให้กับprintฟังก์ชันได้:

print('foo', flush=True) 

Python 2 (หรือ <3.3)

พวกเขาไม่ได้ส่งflushอาร์กิวเมนต์กลับไปที่ Python 2.7 ดังนั้นหากคุณใช้ Python 2 (หรือน้อยกว่า 3.3) และต้องการรหัสที่เข้ากันได้กับทั้ง 2 และ 3 ฉันขอแนะนำรหัสความเข้ากันได้ต่อไปนี้ (โปรดทราบว่า__future__การนำเข้าจะต้องอยู่ที่ / มาก "ใกล้กับด้านบนของโมดูล "):

from __future__ import print_function
import sys

if sys.version_info[:2] < (3, 3):
    old_print = print
    def print(*args, **kwargs):
        flush = kwargs.pop('flush', False)
        old_print(*args, **kwargs)
        if flush:
            file = kwargs.get('file', sys.stdout)
            # Why might file=None? IDK, but it works for print(i, file=None)
            file.flush() if file is not None else sys.stdout.flush()

รหัสการทำงานร่วมกันดังกล่าวจะครอบคลุมการใช้งานมากที่สุด แต่การรักษาอย่างละเอียดมากขึ้นดูsixโมดูล

อีกทางหนึ่งคุณสามารถโทรหาfile.flush()หลังจากพิมพ์ได้ด้วยคำสั่ง print ใน Python 2:

import sys
print 'delayed output'
sys.stdout.flush()

การเปลี่ยนค่าเริ่มต้นในหนึ่งโมดูลเป็น flush=True

คุณสามารถเปลี่ยนค่าเริ่มต้นสำหรับฟังก์ชั่นการพิมพ์โดยใช้ functools.partial ในขอบเขตส่วนกลางของโมดูล:

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

ถ้าคุณดูฟังก์ชันบางส่วนใหม่ของเราอย่างน้อยใน Python 3:

>>> print = functools.partial(print, flush=True)
>>> print
functools.partial(<built-in function print>, flush=True)

เราเห็นว่ามันใช้งานได้เหมือนปกติ:

>>> print('foo')
foo

และเราสามารถแทนที่ค่าเริ่มต้นใหม่ได้จริง:

>>> print('foo', flush=False)
foo

หมายเหตุอีกครั้งนี่เป็นการเปลี่ยนแปลงขอบเขตโกลบอลปัจจุบันเท่านั้นเนื่องจากชื่อการพิมพ์บนขอบเขตโกลบอลปัจจุบันจะมีผลต่อprintฟังก์ชันbuiltin (หรือไม่อ้างอิงฟังก์ชันความเข้ากันได้หากใช้หนึ่งใน Python 2 ในขอบเขตโกลบอลปัจจุบันนั้น)

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

def foo():
    printf = functools.partial(print, flush=True)
    printf('print stuff like this')

หากคุณประกาศให้เป็นโกลบอลในฟังก์ชั่นคุณกำลังเปลี่ยนมันบนเนมสเปซส่วนกลางของโมดูลดังนั้นคุณควรใส่ไว้ในเนมสเปซส่วนกลางยกเว้นว่าพฤติกรรมเฉพาะนั้นเป็นสิ่งที่คุณต้องการ

การเปลี่ยนค่าเริ่มต้นสำหรับกระบวนการ

ฉันคิดว่าตัวเลือกที่ดีที่สุดที่นี่คือการใช้-uแฟล็กเพื่อรับเอาต์พุตที่ไม่มีบัฟเฟอร์

$ python -u script.py

หรือ

$ python -um package.module

จากเอกสาร :

บังคับ stdin, stdout และ stderr ให้เต็มเปา บนระบบที่มีความสำคัญให้ใส่ stdin, stdout และ stderr ในโหมดไบนารี

โปรดทราบว่ามีการบัฟเฟอร์ภายในใน file.readlines () และ File Objects (สำหรับบรรทัดใน sys.stdin) ซึ่งไม่ได้รับอิทธิพลจากตัวเลือกนี้ ในการหลีกเลี่ยงปัญหานี้คุณจะต้องใช้ file.readline () ในขณะที่ 1: ลูป

การเปลี่ยนค่าเริ่มต้นสำหรับสภาพแวดล้อมการทำงานของเชลล์

คุณสามารถรับพฤติกรรมนี้สำหรับกระบวนการหลามทั้งหมดในสภาพแวดล้อมหรือสภาพแวดล้อมที่สืบทอดมาจากสภาพแวดล้อมหากคุณตั้งค่าตัวแปรสภาพแวดล้อมเป็นสตริงที่ไม่ว่างเปล่า:

เช่นใน Linux หรือ OSX:

$ export PYTHONUNBUFFERED=TRUE

หรือ Windows:

C:\SET PYTHONUNBUFFERED=TRUE

จากเอกสาร :

PYTHONUNBUFFERED

หากสิ่งนี้ถูกตั้งค่าเป็นสตริงที่ไม่ว่างจะเท่ากับการระบุอ็อพชัน -u


ภาคผนวก

นี่คือความช่วยเหลือในฟังก์ชั่นการพิมพ์จาก Python 2.7.12 - โปรดทราบว่าไม่มี flushข้อโต้แย้ง:

>>> from __future__ import print_function
>>> help(print)
print(...)
    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.

สำหรับการโยกย้ายที่อยากรู้อยากเห็นจากเวอร์ชัน Python ที่ต่ำกว่า: __future__เวอร์ชันจะไม่รวมflushเพราะ "อาร์กิวเมนต์ flush ถูกเพิ่มใน Python 3.3 (หลังจากพิมพ์ () ถูก backported เป็น 2.7 ผ่านการนำเข้าในอนาคต)" bugs.python.org/issue28458
Oliver

69

นอกจากนี้ตามที่แนะนำในบล็อกนี้หนึ่งสามารถเปิดอีกครั้งsys.stdoutในโหมดที่ไม่มีบัฟเฟอร์:

sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

แต่ละคนstdout.writeและprintการดำเนินการจะถูกล้างโดยอัตโนมัติหลังจากนั้น


2
บน Ubuntu 12.04 ใน python 2.7 นี่ทำให้ฉันUnsupportedOperation: IOStream has no fileno.
drevicko

3
อ๊ะ Python 3 ค้นพบแล้ว มันจะไม่ให้ฉันรันโค้ดชิ้นนี้!
EKons

ฉันสับสนด้วยสำนวนนี้ หลังจากที่คุณทำแล้วตอนนี้ไม่มีวัตถุเหมือนไฟล์สองไฟล์ (sys.stdout ดั้งเดิมและ sys.stdout ใหม่) ที่ทั้งคู่คิดว่าพวกเขา "เป็นเจ้าของ" fileno หรือไม่ ไม่ดีใช่มั้ย
Don Hatch


36

การใช้-uสวิตช์บรรทัดคำสั่งใช้งานได้ แต่มันก็ค่อนข้างงุ่มง่าม หมายความว่าโปรแกรมอาจทำงานผิดพลาดหากผู้ใช้เรียกใช้สคริปต์โดยไม่มี-uตัวเลือก ฉันมักจะใช้ที่กำหนดเองstdoutเช่นนี้

class flushfile:
  def __init__(self, f):
    self.f = f

  def write(self, x):
    self.f.write(x)
    self.f.flush()

import sys
sys.stdout = flushfile(sys.stdout)

... ตอนนี้ทุกprintสาย (ซึ่งใช้sys.stdoutโดยปริยาย) จะได้รับโดยอัตโนมัติflushed


4
ฉันไม่แนะนำให้สืบทอดจากไฟล์จากนั้นมอบหมายให้ stdout โดยการเพิ่ม def __getattr__(self,name): return object.__getattribute__(self.f, name)
เสียชีวิต

2
หากไม่มีการเปลี่ยนแปลงที่เสนอโดยความคิดเห็นโดย @diedthreetimes ฉันจะได้รับ "ValueError: การดำเนินการ I / O บนไฟล์ที่ปิด"
blueFast

19

ทำไมไม่ลองใช้ไฟล์ที่ไม่มีบัฟเฟอร์

f = open('xyz.log', 'a', 0)

หรือ

sys.stdout = open('out.log', 'a', 0)

1
เขาไม่ต้องการให้ ot สร้างไฟล์ที่ไม่มีบัฟเฟอร์ เขาต้องการสร้าง stdout ที่มีอยู่ (เปลี่ยนเส้นทางไปยังคอนโซลเทอร์มินัลหรืออะไรก็ตาม: สิ่งนี้จะต้องไม่ถูกเปลี่ยนแปลง) ไม่มีบัฟเฟอร์
blueFast

13

ความคิดของแดนไม่ได้ผล:

#!/usr/bin/env python
class flushfile(file):
    def __init__(self, f):
        self.f = f
    def write(self, x):
        self.f.write(x)
        self.f.flush()

import sys
sys.stdout = flushfile(sys.stdout)

print "foo"

ผลลัพธ์:

Traceback (most recent call last):
  File "./passpersist.py", line 12, in <module>
    print "foo"
ValueError: I/O operation on closed file

ฉันเชื่อว่าปัญหาคือมันสืบทอดมาจากคลาสไฟล์ซึ่งไม่จำเป็นจริงๆ ตามเอกสารสำหรับ sys.stdout:

stdout และ stderr ไม่จำเป็นต้องเป็นวัตถุไฟล์ในตัว: วัตถุใด ๆ ที่เป็นที่ยอมรับตราบเท่าที่มันมีวิธีการเขียน () ที่ใช้อาร์กิวเมนต์สตริง

ดังนั้นการเปลี่ยนแปลง

class flushfile(file):

ถึง

class flushfile(object):

ทำให้มันใช้งานได้ดี


17
ไม่มีการโหวตเพราะเป็นวิธีการแก้ปัญหา @ แดน ... (คุณควรจะค่อนข้างแสดงความคิดเห็นโพสต์แดนแทนการคัดลอกวิธีการแก้ปัญหาของเขา)
gecco

8

นี่คือเวอร์ชั่นของฉันซึ่งมี writelines () และ fileno () ด้วย:

class FlushFile(object):
    def __init__(self, fd):
        self.fd = fd

    def write(self, x):
        ret = self.fd.write(x)
        self.fd.flush()
        return ret

    def writelines(self, lines):
        ret = self.writelines(lines)
        self.fd.flush()
        return ret

    def flush(self):
        return self.fd.flush

    def close(self):
        return self.fd.close()

    def fileno(self):
        return self.fd.fileno()

โซลูชั่นที่เหนือกว่า และมันใช้งานได้ ทดสอบกับ Python 3.4.0 ด้วยรุ่นอื่นที่ได้รับมาfileฉันได้รับข้อผิดพลาด ไม่มีfileคลาส
Colin D Bennett

6

ใน Python 3 คุณสามารถเขียนทับฟังก์ชั่นการพิมพ์โดยตั้งค่าเริ่มต้นเป็น flush = True

def print(*objects, sep=' ', end='\n', file=sys.stdout, flush=True):
    __builtins__.print(*objects, sep=sep, end=end, file=file, flush=flush)

2
คำตอบนี้ดูเหมือนว่าจะมีแสงสว่างเล็กน้อยสำหรับการตอบกลับคุณภาพสูงอื่น ๆ ทั้งหมด คุณอาจต้องการเพิ่มอีกนิด
อัฒภาคและเทปพันสาย

5

ฉันทำแบบนี้ใน Python 3.4:

'''To write to screen in real-time'''
message = lambda x: print(x, flush=True, end="")
message('I am flushing out now...')

2

ฉันแรกพยายามที่จะเข้าใจวิธีการล้างตัวเลือกการทำงาน ฉันต้องการจะทำ 'loading display' และนี่คือคำตอบที่ฉันพบ:

for i in range(100000):
    print('{:s}\r'.format(''), end='', flush=True)
    print('Loading index: {:d}/100000'.format(i+1), end='')

บรรทัดแรกฟลัชการพิมพ์ก่อนหน้าและบรรทัดที่สองจะพิมพ์ข้อความที่อัพเดตใหม่ ฉันไม่ทราบว่ามีไวยากรณ์หนึ่งบรรทัดอยู่ที่นี่หรือไม่

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