Usando a instrução python “with” com o bloco try-except

96

É esta a maneira certa de usar a instrução python "with" em combinação com um bloco try-except ?:

try:
    with open("file", "r") as f:
        line = f.readline()
except IOError:
    <whatever>

Se for, considerando a velha maneira de fazer as coisas:

try:
    f = open("file", "r")
    line = f.readline()
except IOError:
    <whatever>
finally:
    f.close()

O principal benefício da instrução "with" aqui é que podemos nos livrar de três linhas de código? Não me parece muito atraente para este caso de uso (embora eu entenda que a instrução "com" tenha outros usos).

EDIT: A funcionalidade dos dois blocos de código acima é idêntica?

EDIT2: As primeiras respostas falam geralmente sobre os benefícios de usar "com", mas parecem ter um benefício marginal aqui. Todos nós temos (ou deveríamos ter chamado) explicitamente f.close () por anos. Suponho que um dos benefícios é que programadores desleixados se beneficiarão com o uso de "com".

Gaefan
fonte
Para mim, não ter que lembrar de fechar () as coisas em uma instrução finally é uma razão boa o suficiente para usar 'with'. Tenho visto muitos códigos falhando ao fechar seus recursos. E 'com' não tem desvantagens, tanto quanto posso ver.
Raúl Salinas-Monteagudo

Respostas:

139
  1. Os dois blocos de código que você deu não são equivalentes
  2. O código que você descreveu como a maneira antiga de fazer as coisas tem um bug sério: caso a abertura do arquivo falhe, você obterá uma segunda exceção na finallycláusula porque fnão está vinculado.

O código de estilo antigo equivalente seria:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

Como você pode ver, a withdeclaração pode tornar as coisas menos sujeitas a erros. Nas versões mais recentes do Python (2.7, 3.1), você também pode combinar várias expressões em uma withinstrução. Por exemplo:

with open("input", "r") as inp, open("output", "w") as out:
    out.write(inp.read())

Além disso, pessoalmente considero um mau hábito detectar qualquer exceção o mais cedo possível. Este não é o objetivo das exceções. Se a função IO que pode falhar é parte de uma operação mais complicada, na maioria dos casos o IOError deve abortar toda a operação e, portanto, ser tratado em um nível externo. Usando withdeclarações, você pode se livrar de todas essas try...finallydeclarações em níveis internos.

Bernd Petersohn
fonte
7

Se o conteúdo do finallybloco é determinado pelas propriedades do objeto de arquivo que está sendo aberto, por que não deveria o implementador do objeto de arquivo ser o único a escrever o finallybloco? Esse é o benefício da withinstrução, muito mais do que economizar três linhas de código nesta instância específica.

E sim, a maneira que você combinado withe try-excepté praticamente a única maneira de fazê-lo, como erros excepcionais causado dentro da opendeclaração em si não pode ser capturado dentro do withbloco.

Peter Milley
fonte
1

Acho que você se enganou sobre a afirmação "com" de que ela apenas reduz as linhas. Na verdade, ele faz a inicialização e controla a desmontagem.

No seu caso, "com" significa

  • abrir um arquivo,
  • processar seu conteúdo, e
  • certifique-se de fechá-lo.

Aqui está um link para entender a declaração "com": http://effbot.org/zone/python-with-statement.htm

Editar: Sim, seu uso de "com" está correto e a funcionalidade de ambos os blocos de código é idêntica. Pergunta sobre por que usar "com"? é por causa dos benefícios que você obtém com isso. como você mencionou sobre a falta acidental de f.close ().

YoK
fonte
-4

A maneira mais pitônica para os seguintes códigos é:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

try:
    f = open("file", "r")
except IOError:
    <whatever>
else:
    f.close()
Leo Liu
fonte
1
Eu adicionei a formatação de código para você; torna mais fácil de ler. Mas você pode querer verificar novamente para ter certeza de que não quebrei o recuo.
andrewsi
2
Não, sua versão não faz a mesma coisa que o código original. Mesmo se você adicionar a readline()chamada ausente , sua versão não fecha o arquivo se o readline()resultado for um IOError.
Aleksi Torhamo