Há exatamente um motivo pelo qual o seguinte é preferido:
with open('filename.txt') as fp:
for line in fp:
print line
Todos nós somos prejudicados pelo esquema relativamente determinístico de contagem de referência do CPython para coleta de lixo. Outras implementações hipotéticas do Python não necessariamente fecham o arquivo "com rapidez suficiente" sem owith
bloco se eles usarem algum outro esquema para recuperar a memória.
Em tal implementação, você pode receber um erro "muitos arquivos abertos" no sistema operacional se o seu código abrir arquivos mais rapidamente do que o coletor de lixo chama finalizadores em identificadores de arquivos órfãos. A solução usual é acionar o GC imediatamente, mas esse é um truque desagradável e deve ser feito por todos os funções que possam encontrar o erro, incluindo as das bibliotecas. Que pesadelo.
Ou você pode simplesmente usar o with
bloco.
Pergunta bônus
(Pare de ler agora se estiver interessado apenas nos aspectos objetivos da pergunta.)
Por que isso não está incluído no protocolo do iterador para objetos de arquivo?
Esta é uma pergunta subjetiva sobre o design da API, por isso tenho uma resposta subjetiva em duas partes.
No nível interno, isso parece errado, porque faz com que o protocolo do iterador faça duas coisas separadas - iterar sobre linhas e feche o identificador do arquivo - e geralmente é uma má idéia fazer uma função de aparência simples executar duas ações. Nesse caso, parece especialmente ruim porque os iteradores se relacionam de maneira quase funcional e baseada em valores com o conteúdo de um arquivo, mas o gerenciamento de identificadores de arquivo é uma tarefa completamente separada. Esmagar os dois, invisivelmente, em uma ação, é surpreendente para os humanos que lêem o código e torna mais difícil raciocinar sobre o comportamento do programa.
Outras línguas chegaram essencialmente à mesma conclusão. Haskell flertou brevemente com o chamado "IO preguiçoso", que permite iterar sobre um arquivo e fechá-lo automaticamente quando você chegar ao final do fluxo, mas é quase universalmente desencorajado o uso de IO preguiçoso em Haskell atualmente e Haskell os usuários passaram para o gerenciamento de recursos mais explícito, como o Conduit, que se comporta mais como o with
bloco do Python.
Em um nível técnico, há algumas coisas que você pode querer fazer com um identificador de arquivo no Python que não funcionaria tão bem se a iteração fechasse o identificador de arquivo. Por exemplo, suponha que eu precise repetir o arquivo duas vezes:
with open('filename.txt') as fp:
for line in fp:
...
fp.seek(0)
for line in fp:
...
Embora este seja um caso de uso menos comum, considere o fato de que talvez eu tenha adicionado as três linhas de código na parte inferior a uma base de código existente que originalmente possuía as três principais linhas. Se a iteração fechou o arquivo, eu não seria capaz de fazer isso. Portanto, manter a iteração e o gerenciamento de recursos separados facilita a composição de trechos de código em um programa Python maior e funcional.
A composição é um dos recursos de usabilidade mais importantes de um idioma ou API.