Como liberar a saída da função de impressão?

1216

Como forçar a função de impressão do Python a ser exibida na tela?

Isso não é uma duplicata do Desativar buffer de saída - a questão vinculada está tentando saída sem buffer, enquanto isso é mais geral. As principais respostas nessa pergunta são muito poderosas ou envolvidas para essa (não são boas respostas para isso), e essa pergunta pode ser encontrada no Google por um novato.

Walter Nissen
fonte

Respostas:

1428

No Python 3, printpode-se usar um flushargumento opcional

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

No Python 2 você terá que fazer

import sys
sys.stdout.flush()

depois de ligar print. Por padrão, printimprime em sys.stdout(consulte a documentação para obter mais informações sobre objetos de arquivo ).

CesarB
fonte
347

Em execução python -h, vejo uma opção de linha de comando :

-u: stdout binário sem buffer e stderr; also PYTHONUNBUFFERED = x consulte a página de manual para obter detalhes sobre o buffer interno relacionado a '-u'

Aqui está o documento relevante .

gimel
fonte
320

Desde o Python 3.3, você pode forçar a print()função normal a liberar sem a necessidade de usar sys.stdout.flush(); basta definir o argumento da palavra-chave "flush" como true. A partir da documentação :

print (* objetos, sep = '', final = '\ n', arquivo = sys.stdout, flush = False)

Imprima objetos no arquivo de fluxo, separados por setembro e seguidos por final. sep, end e file, se presentes, devem ser fornecidos como argumentos de palavra-chave.

Todos os argumentos que não são de palavra-chave são convertidos em seqüências de caracteres como str () e gravados no fluxo, separados por sep e seguidos por end. Sep e end devem ser strings; eles também podem ser Nenhum, o que significa usar os valores padrão. Se nenhum objeto for fornecido, print () apenas escreverá end.

O argumento do arquivo deve ser um objeto com um método write (string); se não estiver presente ou Nenhum, sys.stdout será usado. Se a saída é armazenada em buffer geralmente é determinada pelo arquivo, mas se o argumento de palavra-chave de descarga for verdadeiro, o fluxo será liberado à força.

Eugene Sajine
fonte
198

Como liberar a saída da impressão Python?

Sugiro cinco maneiras de fazer isso:

  • No Python 3, chame print(..., flush=True)(o argumento flush não está disponível na função de impressão do Python 2 e não há analógico para a instrução print).
  • Chame file.flush()o arquivo de saída (podemos envolver a função de impressão do python 2 para fazer isso), por exemplo,sys.stdout
  • aplique isso a todas as chamadas de função de impressão no módulo com uma função parcial,
    print = partial(print, flush=True)aplicada ao módulo global.
  • aplique isso ao processo com uma flag ( -u) passada para o comando intérprete
  • aplique isso a todos os processos python em seu ambiente com PYTHONUNBUFFERED=TRUE(e desative a variável para desfazer isso).

Python 3.3 ou superior

Usando o Python 3.3 ou superior, você pode apenas fornecer flush=Truecomo argumento de palavra-chave a printfunção:

print('foo', flush=True) 

Python 2 (ou <3.3)

Eles não suportaram o flushargumento no Python 2.7. Portanto, se você estiver usando o Python 2 (ou menos do que 3.3) e quiser um código compatível com o 2 e o 3, sugiro o seguinte código de compatibilidade. (Observe que a __future__importação deve estar / muito "perto da parte superior do seu módulo "):

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

O código de compatibilidade acima cobrirá a maioria dos usos, mas para um tratamento muito mais completo, consulte o sixmódulo .

Como alternativa, você pode simplesmente ligar file.flush()após a impressão, por exemplo, com a instrução print no Python 2:

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

Alterando o padrão em um módulo para flush=True

Você pode alterar o padrão da função de impressão usando functools.partial no escopo global de um módulo:

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

se você olhar para a nossa nova função parcial, pelo menos no Python 3:

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

Podemos ver que funciona como normal:

>>> print('foo')
foo

E podemos substituir o novo padrão:

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

Observe novamente que isso altera apenas o escopo global atual, porque o nome de impressão no escopo global atual ofuscará a printfunção interna (ou não fará referência à função de compatibilidade, se estiver usando uma no Python 2, nesse escopo global atual).

Se você deseja fazer isso dentro de uma função, em vez de no escopo global de um módulo, deve atribuir um nome diferente, por exemplo:

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

Se você o declarar global em uma função, você o está alterando no espaço para nome global do módulo, portanto, você deve colocá-lo no espaço para nome global, a menos que esse comportamento específico seja exatamente o que você deseja.

Alterando o padrão para o processo

Eu acho que a melhor opção aqui é usar a -uflag para obter uma saída sem buffer.

$ python -u script.py

ou

$ python -um package.module

Dos documentos :

Força stdin, stdout e stderr a serem totalmente inalterados. Nos sistemas onde importa, coloque também stdin, stdout e stderr no modo binário.

Observe que há buffer interno em file.readlines () e objetos de arquivo (para linha em sys.stdin) que não é influenciado por esta opção. Para contornar isso, convém usar file.readline () dentro de um tempo 1: loop.

Alterando o Padrão para o Ambiente Operacional Shell

Você pode obter esse comportamento para todos os processos python no ambiente ou ambientes herdados do ambiente, se você definir a variável de ambiente como uma sequência não vazia:

por exemplo, no Linux ou OSX:

$ export PYTHONUNBUFFERED=TRUE

ou Windows:

C:\SET PYTHONUNBUFFERED=TRUE

dos documentos :

PYTHONUNBUFFERED

Se isso estiver definido como uma sequência não vazia, será equivalente a especificar a opção -u.


Termo aditivo

Aqui está a ajuda na função de impressão do Python 2.7.12 - observe que não flush argumento:

>>> 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
fonte
Para a migração curiosa de versões mais baixas do Python: a __future__versão não inclui flushporque "o argumento de flush foi adicionado no Python 3.3 (após a impressão () ter sido suportada para 2.7 através de uma importação futura)" bugs.python.org/issue28458
Oliver
69

Também como sugerido neste blog, é possível reabrir sys.stdoutno modo sem buffer:

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

Cada operação stdout.writee printserá automaticamente liberada depois.

Antony Hatchkins
fonte
2
No Ubuntu 12.04 em python 2.7, isso me dá UnsupportedOperation: IOStream has no fileno.
#
3
Opa, o Python 3 descobriu. Não me permitirá executar este pedaço de código!
EKons
Estou confuso com esse idioma. Depois de fazer isso, não existem agora dois objetos semelhantes a arquivos (o sys.stdout original e o novo sys.stdout) que ambos pensam que "possuem" o fileno? Isso é ruim, né?
Don escotilha
62

Com o Python 3.x, a print()função foi estendida:

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

Então, você pode fazer:

print("Visiting toilet", flush=True)

Entrada de documentos do Python

Noah Krasser
fonte
36

O uso da -uopção de linha de comando funciona, mas é um pouco desajeitado. Isso significaria que o programa potencialmente se comportaria incorretamente se o usuário invocasse o script sem a -uopção Eu costumo usar um costume stdout, assim:

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)

... Agora todas as suas printchamadas (que são usadas sys.stdoutimplicitamente) serão automaticamente flusheditadas.

Dan Lenski
fonte
4
Eu recomendo não herdar do arquivo e delegar ao stdout adicionando. def __getattr__(self,name): return object.__getattribute__(self.f, name)
diedthreetimes
2
Sem as alterações sugeridas pelo comentário por @diedthreetimes, eu recebo "ValueError: operação de I / O em arquivo fechado"
blueFast
19

Por que não tentar usar um arquivo sem buffer?

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

OU

sys.stdout = open('out.log', 'a', 0)
Nakilon
fonte
1
Ele não deseja criar um arquivo sem buffer; ele quer tornar o stdout existente (redirecionado para o console, o terminal ou o que for: isso não deve ser alterado) sem buffer.
blueFast
13

A ideia de Dan não funciona muito bem:

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

O resultado:

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

Acredito que o problema é que ele herda da classe de arquivo, o que na verdade não é necessário. De acordo com os documentos para sys.stdout:

stdout e stderr não precisam ser objetos de arquivo internos: qualquer objeto é aceitável desde que tenha um método write () que use um argumento string.

tão mudando

class flushfile(file):

para

class flushfile(object):

faz funcionar muito bem.

Kamil Kisiel
fonte
17
Nenhum voto porque este é @ solução de Dan ... (Você deve, antes comentar o post de Dan em vez de copiar a sua solução)
Gecco
8

Aqui está minha versão, que fornece writelines () e fileno () também:

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
fonte
Solução superior. E isso funciona. Testado em Python 3.4.0. Nas outras versões derivadas file, recebo um erro. Não há fileaula.
Colin D Bennett
6

No Python 3, você pode sobrescrever a função de impressão com o padrão definido como flush = True

def print(*objects, sep=' ', end='\n', file=sys.stdout, flush=True):
    __builtins__.print(*objects, sep=sep, end=end, file=file, flush=flush)
user263387
fonte
2
essa resposta parece um pouco de luz, considerando todas as outras respostas de alta qualidade. você pode adicionar um pouco mais a ele.
Ponto e vírgula e fita adesiva
5

Eu fiz assim no Python 3.4:

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

Primeiro, lutei para entender como a opção de descarga estava funcionando. Eu queria fazer um 'display de carregamento' e aqui está a solução que encontrei:

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

A primeira linha limpa a impressão anterior e a segunda linha imprime uma nova mensagem atualizada. Não sei se existe uma sintaxe de uma linha aqui.

Guillaume Mougeot
fonte