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


306

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

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

то же самое с этим, используя finally:

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

Я что-то упускаю?

Ответы:


422

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

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.

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

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

1
try: #x = Hello + 20 x = 10 + 20 кроме: print «Я в другом месте, кроме блока» x = 20 + 30 else: print «Я в другом блоке» x + = 1 finally: print «В конце x =% s '% (x)
Абхиджит Саху

89

Вы можете использовать, 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которое читатели могут найти забавным.


23

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


15
Finally code is run no matter what else happens... если нет бесконечного цикла. Или powercut. Или os._exit(). Или ...
Марк Байерс

3
@Mark На самом деле, sys.exit выдает нормальное исключение. Но да, все, что вызывает немедленное завершение процесса, будет означать, что больше ничего не выполняется.
Сурьма

1
@Antimony: спасибо. Изменено на os._exit.
Марк Байерс

Просто интересно, почему код очистки не может быть помещен внутри, если только код не будет введен, кроме как только в случае обнаружения исключения?
Стивен Джейкоб

2
@Stephen С одной стороны, наконец, код выполняется, даже если вы вернетесь из блока try. В этом случае вы не нажмете пункт «исключение».
Сурьма

18

Чтобы добавить к другим ответам выше, 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предложение выполняется независимо от исключения. Надеюсь это поможет.


2
Это сработало бы, даже если бы вы не использовали предложение «finally», которое не отвечает на вопрос, так как OP хочет знать разницу, хороший пример вызвал бы ошибку, отличную от IOError, чтобы показать, что Блок предложения finally выполняется до того, как исключение передается вызывающей стороне.
Реда Дрисси

2
Я не знал, elseбыла вещь. Полезно знать.
мазуньки

8

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


7

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

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


7

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

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


4

Прекрасный пример как ниже:

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)

3

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

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


2

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

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


2

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

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

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

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

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

2

Блок try имеет только одно обязательное условие: оператор try. Предложения exc, else и finally не являются обязательными и основаны на предпочтениях пользователя.

finally: Прежде чем Python покинет оператор try, он выполнит код в блоке finally при любых условиях, даже если завершает программу. Например, если Python столкнулся с ошибкой во время выполнения кода в блоке "else" или "else", блок finally будет по-прежнему выполняться перед остановкой программы.


1
Это не верно. Оператор исключения является обязательным. - Лукас Азеведо, 1 февраля в 12:04 Это неправильно, так как я только что скомпилировал и запустил программу на Python 3.5 с блоком try-finally, без условия «кроме».
Роб

2
Я попробовал это сам и, к моему неверию, пункт «исключение» не является обязательным.
капитан черный

1

Запустите эти коды Python3, чтобы увидеть необходимость наконец:

СЛУЧАЙ 1:

count = 0
while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")
        finally:
            print("Your Attempts: {}".format(count))

Вариант 2:

count = 0

while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")

        print("Your Attempts: {}".format(count))

Попробуйте каждый раз вводить следующие данные:

  1. случайные целые числа
  2. правильный код 586 (попробуйте, и вы получите ответ)
  3. случайные строки

** На очень ранней стадии изучения Python.


1

Я пытался запустить код, где я хотел читать листы Excel. Проблема была, если есть файл, у которого нет листа с именем скажем: SheetSum Я не могу переместить его в место ошибки !! Код, который я написал, был:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Ошибка выдачи:

[WinError 32] Процесс не может получить доступ к файлу, потому что он используется другим процессом

Я должен был добавить полный try except with finallyблок и сказать, что finallyмне нужно закрыть файл в любом случае, как:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

В противном случае файл по-прежнему остается открытым фоном.

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

..Больше здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.