Jak opróżnić wyjście funkcji drukowania?

1215

Jak zmusić funkcję drukowania Pythona do wyświetlania na ekranie?

Nie jest to duplikat Wyłącz buforowanie wyjściowe - połączone pytanie próbuje niebuforowanego wyjścia, ale jest to bardziej ogólne. Najważniejsze odpowiedzi w tym pytaniu są na to zbyt mocne lub zbyt skomplikowane (nie są to dobre odpowiedzi na to pytanie), a to pytanie może znaleźć w Google względny początkujący.

Walter Nissen
źródło

Odpowiedzi:

1426

W Pythonie 3 printmożna przyjąć opcjonalny flushargument

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

W Pythonie 2 musisz to zrobić

import sys
sys.stdout.flush()

po zadzwonieniu print. Domyślnie printdrukuje do sys.stdout(więcej informacji na temat obiektów plików można znaleźć w dokumentacji ).

CesarB
źródło
346

Bieganie python -h, widzę opcji wiersza poleceń :

-u: niebuforowane standardowe binarne i standardowe; również PYTHONUNBUFFERED = x patrz strona podręcznika dla szczegółowych informacji o wewnętrznym buforowaniu odnoszącym się do '-u'

Oto odpowiedni dokument .

gimel
źródło
320

Od wersji Python 3.3 możesz zmusić normalną print()funkcję do opróżnienia bez potrzeby używania sys.stdout.flush(); po prostu ustaw argument słowa kluczowego „flush” na true. Z dokumentacji :

print (* obiekty, sep = '', end = '\ n', file = sys.stdout, flush = False)

Wydrukuj obiekty do pliku strumieniowego, oddzielone sep, a następnie end. sep, end i file, jeśli są obecne, muszą być podane jako argumenty słów kluczowych.

Wszystkie argumenty niebędące słowami kluczowymi są konwertowane na ciągi takie jak str () i zapisywane do strumienia, oddzielane sep, a następnie zakończone. Zarówno sep, jak i koniec muszą być łańcuchami; mogą być również Brak, co oznacza użycie wartości domyślnych. Jeśli nie podano żadnych obiektów, print () po prostu zapisze koniec.

Argument pliku musi być obiektem z metodą write (string); jeśli nie jest obecny lub Brak, zostanie użyty sys.stdout. To, czy dane wyjściowe są buforowane, jest zwykle określane przez plik, ale jeśli argument słowa kluczowego flush jest prawdziwy, strumień jest wymuszany.

Eugene Sajine
źródło
197

Jak opróżnić wydruk Pythona?

Proponuję pięć sposobów:

  • W Pythonie 3 wywołaj print(..., flush=True)(argument flush nie jest dostępny w funkcji drukowania Pythona 2 i nie ma analogii dla instrukcji print).
  • Wywołaj file.flush()plik wyjściowy (możemy w tym celu owinąć funkcję drukowania Pythona 2), na przykład:sys.stdout
  • zastosuj to do każdego wywołania funkcji drukowania w module z funkcją częściową,
    print = partial(print, flush=True)zastosowaną do modułu globalnie.
  • zastosuj to do procesu za pomocą flagi ( -u) przekazanej do polecenia interpretera
  • zastosuj to do każdego procesu Pythona w swoim środowisku za pomocą PYTHONUNBUFFERED=TRUE(i odznacz zmienną, aby to cofnąć).

Python 3.3+

Używając Pythona w wersji 3.3 lub nowszej, możesz po prostu podać flush=Truejako słowo kluczowe argument printfunkcji:

print('foo', flush=True) 

Python 2 (lub <3.3)

Nie przeniosły flushargumentu do Pythona 2.7. Jeśli więc używasz języka Python 2 (lub mniejszego niż 3.3) i chcesz kodu zgodnego zarówno z 2, jak i 3, proponuję następujący kod zgodności. (Uwaga: __future__import musi znajdować się w / bardzo „u góry modułu ”):

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

Powyższy kod zgodności obejmie większość zastosowań, ale w celu dokładniejszego zapoznania się z sixmodułem .

Alternatywnie możesz po prostu zadzwonić file.flush()po wydrukowaniu, na przykład za pomocą instrukcji print w Pythonie 2:

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

Zmiana wartości domyślnej w jednym module na flush=True

Możesz zmienić wartość domyślną dla funkcji drukowania, używając funools.partial w globalnym zakresie modułu:

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

jeśli spojrzysz na naszą nową funkcję częściową, przynajmniej w Pythonie 3:

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

Widzimy, że działa tak jak normalnie:

>>> print('foo')
foo

I możemy faktycznie zastąpić nową wartość domyślną:

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

Uwaga: zmienia to tylko bieżący zakres globalny, ponieważ nazwa wydruku w bieżącym zakresie globalnym przesłania wbudowaną printfunkcję (lub nie odwołuje się do funkcji zgodności, jeśli używa się jej w Pythonie 2, w tym bieżącym zakresie globalnym).

Jeśli chcesz to zrobić wewnątrz funkcji zamiast w globalnym zasięgu modułu, powinieneś nadać jej inną nazwę, np .:

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

Jeśli deklarujesz, że jest funkcją globalną w funkcji, zmieniasz ją w globalnej przestrzeni nazw modułu, więc powinieneś po prostu umieścić ją w globalnej przestrzeni nazw, chyba że to konkretne zachowanie jest dokładnie tym, czego chcesz.

Zmiana wartości domyślnej dla procesu

Myślę, że najlepszą opcją jest użycie -uflagi, aby uzyskać niebuforowane wyjście.

$ python -u script.py

lub

$ python -um package.module

Z dokumentów :

Zmuś stdin, stdout i stderr do całkowitego niebuforowania. W systemach, w których ma to znaczenie, ustaw także stdin, stdout i stderr w trybie binarnym.

Zauważ, że w plikach.readlines () i obiektach plików (dla linii w sys.stdin) istnieje wewnętrzne buforowanie, na które ta opcja nie ma wpływu. Aby obejść ten problem, należy użyć file.readline () wewnątrz pętli while 1:.

Zmienianie wartości domyślnej dla środowiska operacyjnego powłoki

Możesz uzyskać takie zachowanie dla wszystkich procesów Pythona w środowisku lub środowiskach dziedziczących po środowisku, jeśli ustawisz zmienną środowiskową na niepusty ciąg:

np. w systemie Linux lub OSX:

$ export PYTHONUNBUFFERED=TRUE

lub Windows:

C:\SET PYTHONUNBUFFERED=TRUE

z dokumentów :

PYTHONUNBUFFERED

Jeśli ustawiono niepusty ciąg znaków, jest to równoważne z określeniem opcji -u.


Uzupełnienie

Oto pomoc na temat funkcji drukowania z Python 2.7.12 - zauważ, że nie ma flush argumentu:

>>> 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.
Aaron Hall
źródło
Dla ciekawskich migrujących z niższych wersji Pythona: __future__wersja nie zawiera, flushponieważ „argument Python został dodany w Pythonie 3.3 (po tym, jak print () został przeniesiony do wersji 2.7 za pomocą przyszłego importu)” bugs.python.org/issue28458
Oliver
69

Również zgodnie z sugestią na tym blogu można ponownie otworzyć sys.stdoutw trybie niebuforowanym:

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

Każda stdout.writei printoperacja zostanie automatycznie spłukuje później.

Antony Hatchkins
źródło
2
Na Ubuntu 12.04 w Pythonie 2.7 daje mi toUnsupportedOperation: IOStream has no fileno.
drevicko
3
Ups, Python 3 się dowiedział. Nie pozwoli mi to wykonać tego kodu!
EKons
Jestem zdezorientowany tym idiomem. Czy po wykonaniu tej czynności nie ma już dwóch obiektów podobnych do pliku (oryginalnego sys.stdout i nowego sys.stdout), które uważają, że „posiadają” fileno? To źle, prawda?
Don Hatch,
62

W Pythonie 3.x print()funkcja została rozszerzona:

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

Możesz po prostu zrobić:

print("Visiting toilet", flush=True)

Python Docs Entry

Noah Krasser
źródło
36

Korzystanie z -uprzełącznika wiersza polecenia działa, ale jest nieco niezdarne. Oznaczałoby to, że program potencjalnie zachowywałby się niepoprawnie, gdyby użytkownik wywołał skrypt bez -uopcji. Zwykle używam niestandardowego stdout, takiego jak to:

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)

... Teraz wszystkie twoje printrozmowy (które będą używane sys.stdoutniejawnie) zostaną automatycznie flushedytowane.

Dan Lenski
źródło
4
Nie polecam dziedziczenia z pliku, a następnie delegowania na standardowe wyjście poprzez dodanie. def __getattr__(self,name): return object.__getattribute__(self.f, name)
deadthreetimes
2
Bez zmian sugerowanych przez komentarzem @diedthreetimes, otrzymuję „ValueError: operacja We / Wy na zamkniętym pliku”
blueFast
19

Dlaczego nie spróbować użyć niebuforowanego pliku?

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

LUB

sys.stdout = open('out.log', 'a', 0)
Nakilon
źródło
1
Nie chce tworzyć niebuforowanego pliku; chce, aby istniejący stdout (przekierowany do konsoli, terminala itp.) nie był buforowany.
blueFast
13

Pomysł Dana nie działa:

#!/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"

Wynik:

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

Uważam, że problem polega na tym, że dziedziczy on po klasie plików, co w rzeczywistości nie jest konieczne. Według dokumentacji dla sys.stdout:

stdout i stderr nie muszą być wbudowanymi obiektami plików: dowolny obiekt jest dopuszczalny, o ile ma metodę write (), która pobiera argument ciągu.

tak się zmienia

class flushfile(file):

do

class flushfile(object):

sprawia, że ​​działa dobrze.

Kamil Kisiel
źródło
17
Nie głosuj, ponieważ to rozwiązanie IS @ Dan ... (Powinieneś raczej skomentować post Dana zamiast kopiować jego rozwiązanie)
gecco 15.01.2013
8

Oto moja wersja, która zawiera również writelines () i 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()
guettli
źródło
Doskonałe rozwiązanie. I to działa. Testowane na Pythonie 3.4.0. W przypadku innych wersji, które pochodzą file, pojawia się błąd. Nie ma fileklasy.
Colin D Bennett,
6

W Pythonie 3 możesz zastąpić funkcję drukowania domyślnie ustawioną na flush = True

def print(*objects, sep=' ', end='\n', file=sys.stdout, flush=True):
    __builtins__.print(*objects, sep=sep, end=end, file=file, flush=flush)
użytkownik263387
źródło
2
ta odpowiedź wydaje się trochę lekka, biorąc pod uwagę wszystkie inne wysokiej jakości odpowiedzi. możesz dodać trochę więcej.
Średnik i taśma
5

Tak zrobiłem w Pythonie 3.4:

'''To write to screen in real-time'''
message = lambda x: print(x, flush=True, end="")
message('I am flushing out now...')
kmario23
źródło
2

Najpierw starałem się zrozumieć, jak działa opcja koloru. Chciałem zrobić „ładowanie ekranu” i oto rozwiązanie, które znalazłem:

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

Pierwszy wiersz opróżnia poprzedni wydruk, a drugi wiersz drukuje nową zaktualizowaną wiadomość. Nie wiem, czy istnieje tutaj składnia jednowierszowa.

Guillaume Mougeot
źródło