Зачем нам нужно предложение "finally" в Python?


Я не уверен, зачем нужны finally на try...except...finally заявления. На мой взгляд, этот блок кода

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

то же самое с этим с помощью finally:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

Я что-то пропустила?

11 175

11 ответов:

это имеет значение, если вы вернетесь рано:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

сравнить с этим:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

другие ситуации, которые могут вызвать различия:

  • если исключение создается внутри блока except.
  • если возникает исключение run_code1() но это не TypeError.
  • другие операторы потока управления, такие как continue и break заявления.

можно использовать finally чтобы убедиться, что файлы или ресурсы закрыты или освобождены независимо от того, происходит ли исключение, даже если вы не поймать исключение. (или если вы не поймать конкретные исключения.)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

в этом примере вам было бы лучше использовать with заявление, но этот вид структуры может быть использован для других видов ресурсов.

несколько лет спустя, я написал блоге о злоупотребление finally что читатели могут найти забавным.

Они не эквивалентны. Наконец, код выполняется независимо от того, что еще происходит. Это полезно для кода очистки, который должен выполняться.

блоки кода не эквивалентны. Элемент finally предложение также будет выполняться, если run_code1() создает исключение, отличное от TypeError, или run_code2() выдает исключение, в то время как other_code() в первой версии не будет работать в этих случаях.

в вашем первом примере, что произойдет, если run_code1() вызывает исключение, а не TypeError? ... other_code() не будет выполнен.

сравните это с finally: версия: other_code() гарантированно будет выполняться независимо от любого возникшего исключения.

чтобы добавить к другим ответам выше,finally предложение выполняется независимо от того, что тогда как else предложение выполняется только в том случае, если исключение не было вызвано.

например, при записи в файл без исключений будет выведено следующее:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

выход:

Writing to file.
Write successful.
File closed.

если есть исключение, код выведет следующее, (обратите внимание, что преднамеренная ошибка вызвана сохранение файла только для чтения.

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

выход:

Could not write to file.
File closed.

мы видим, что finally предложение выполняется независимо от исключения. Надеюсь, это поможет.

finally для определения "очистить действия". Элемент finally предложение выполняется в любом случае перед выходом из try оператор, произошло ли исключение (даже если вы его не обрабатываете) или нет.

Я второй пример @Байерса.

наконец, можно также использовать, когда вы хотите запустить "необязательный" код перед запуском кода для своей основной работы, и этот необязательный код может потерпеть неудачу по разным причинам.

в следующем примере, мы не знаем точно, какие исключения store_some_debug_info могут кинуть.

мы могли бы запустить:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

но большинство линтеров будут жаловаться на слишком расплывчатое исключение. Кроме того, поскольку мы выбираем просто pass по ошибки,except блок не действительно добавить ценность.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

приведенный выше код имеет тот же эффект, как и 1-й блок кода, но более лаконично.

прекрасный пример, как показано ниже:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)

как поясняется в документация на finally предложение предназначено для определения действий очистки, которые должны быть выполнены при всех обстоятельствах.

если finally присутствует, он указывает обработчик "очистка". Элемент try предложение выполняется, в том числе любое except и else положения. Если исключение возникает в любом из предложений и не обрабатывается, исключение-временно сохраняется. Элемент finally пункт выполняется. Если существует сохраненное исключение, которое повторно вызывается в конце finally пункт.

пример:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

как вы можете видеть,finally пункт выполняется в любом случае. Элемент TypeError поднял путем деления двух строк не обрабатывается except предложение и поэтому повторно подняты после finally предложение было выполнено.

в реальных приложениях предложение finally полезно для освобождения внешних ресурсов (таких как файлы или сетевые подключения), независимо от того, было ли использование ресурса успешным.

использование delphi профессионально в течение нескольких лет научило меня защищать мои процедуры очистки, используя, наконец,. Delphi в значительной степени обеспечивает использование finally для очистки любых ресурсов, созданных перед блоком try, чтобы не вызвать утечку памяти. Это также, как Java, Python и Ruby работает.

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

и ресурс будет очищен независимо от того, что вы делаете между попробовать и наконец. Кроме того, он не будет очищен, если выполнение никогда не достигает try блок. (т. е. бросает исключение) это делает ваш код "исключений".

А почему вы, собственно, нужен блок finally, не все языки. В C++, где вы автоматически вызываете деструкторы, которые принудительно очищают, когда исключение разворачивает стек. Я думаю, что это шаг в направлении более чистого кода по сравнению с try...наконец языки.

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.