Зачем нам нужно предложение "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 ответов


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

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 что читатели могут найти забавным.


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


блоки кода не эквивалентны. The 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 для определения "очистить действия". The finally предложение выполняется в любом случае перед выходом из try оператор, произошло ли исключение (даже если вы не обрабатываете его) или нет.

Я второй пример @Byers.


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

в следующем примере, мы не знаем точно, какие исключения 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)

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

если finally присутствует, он указывает обработчик ‘cleanup’. The try предложение выполняется, в том числе any except и else положения. Если исключение возникает в любом из предложений и не обрабатывается, исключение-временно сохраняется. The 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 пункт выполняется в любом случае. The 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.