Як відбити функцію друку?


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 див. головну сторінку для отримання детальної інформації про внутрішню буферизацію, що стосується '-u'

Ось відповідний док .


320

Оскільки Python 3.3, ви можете змусити нормальну print()функцію спалахнути без потреби sys.stdout.flush(); просто встановіть для аргументу ключового слова "flush" значення true. З документації :

print (* об'єкти, sep = '', end = '\ n', file = sys.stdout, flush = Невірно)

Роздрукуйте об'єкти у файлі потоку, відокремленому sep та наступним кінцем. sep, end та файл, якщо вони є, повинні бути наведені як аргументи ключових слів.

Всі аргументи, що не належать до ключових слів, перетворюються на рядки, такі як str (), і записуються в потік, розділяючись sep і слідом за кінцем. І сеп, і кінець повинні бути рядками; вони також можуть бути None, що означає використовувати значення за замовчуванням. Якщо жодних об’єктів не задано, print () просто запише кінець.

Аргумент файлу повинен бути об'єктом методом write (string); якщо його немає або немає, буде використано sys.stdout. Буферизація виводу зазвичай визначається файлом, але якщо аргумент ключового слова флеш істинний, потік примусово промивається.


197

Як відмити висновок друку Python?

Я пропоную п'ять способів зробити це:

  • У Python 3 зателефонуйте print(..., flush=True)(аргумент flush недоступний у функції друку Python 2, і аналог для оператора print не існує).
  • Зателефонуйте file.flush()на вихідний файл (для цього ми можемо зафіксувати функцію друку python 2), наприклад,sys.stdout
  • застосувати це до кожного виклику функції друку в модулі з частковою функцією,
    print = partial(print, flush=True)застосованої до модуля глобального.
  • застосувати це до процесу за допомогою прапора ( -u), переданого команді інтерпретатора
  • застосуйте це до кожного процесу python у вашому оточенні 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()після друку, наприклад, із заявою друку на 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функцію (або не відміняти функцію сумісності, якщо ви використовуєте її в 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 File (для рядка в sys.stdin) є внутрішня буферизація, на яку ця опція не впливає. Щоб вирішити це, вам потрібно буде використовувати файл.readline () всередині 1: цикл.

Зміна за замовчуванням для операційного середовища оболонки

Ви можете отримати таку поведінку для всіх процесів python у середовищі чи середовищах, які успадковують оточення, якщо встановити змінну оточення на непорожній рядок:

наприклад, в Linux або OSX:

$ export PYTHONUNBUFFERED=TRUE

або Windows:

C:\SET PYTHONUNBUFFERED=TRUE

з документів :

ПІТОНУНБУФЕР

Якщо для цього встановлено не порожню рядок, це рівнозначно параметру -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 (після того, як print () було підтримано в 2.7 через майбутній імпорт)" bugs.python.org/issue28458
Олівер

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), які обидва думають, що вони "володіють" файлом? Це погано, правда?
Дон Хетч


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неявно) будуть автоматично flushвідредаговані.


4
Я рекомендую не успадковувати з файлу, а потім делегувати до stdout шляхом додавання. def __getattr__(self,name): return object.__getattribute__(self.f, name)
померли періоди

2
Без змін, запропонованих коментарем @diedthreasures, я отримую "ValueError: Операція вводу / виводу на закритому файлі"
blueFast

19

Чому б не спробувати використовувати небуферований файл?

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

АБО

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

1
Він не хоче, щоб від нього створювався незабудований файл; він хоче зробити існуючий 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 не повинні бути вбудованими файловими об'єктами: будь-який об'єкт є прийнятним, якщо у нього є метод write (), який бере аргумент рядка.

так змінюється

class flushfile(file):

до

class flushfile(object):

змушує це працювати просто чудово.


17
Не голосуйте, оскільки це рішення ІС @ Дана ... (Ви краще коментувати публікацію Дана, а не копіювати його рішення)
gecco

8

Ось моя версія, яка також містить писання () і 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Класу немає .
Колін Д Беннетт

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

Я спершу намагався зрозуміти, як працює опція змиву. Я хотів зробити "дисплей завантаження", і ось знайдене нами рішення:

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

Перший рядок стирає попередній друк, а другий рядок друкує нове оновлене повідомлення. Я не знаю, чи існує тут однорядковий синтаксис.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.