Для чого нам потрібна стаття "остаточно" в 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
спробуйте: # x = Привіт + 20 х = 10 + 20, за винятком: надрукувати "Я в, за винятком блоку" х = 20 + 30 ще: друкувати "Я в блоці ще" x + = 1 нарешті: надрукувати "Нарешті x =% s '% (x)
Abhijit Sahu

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... якщо тільки не існує нескінченна петля. Або електрощит. Або os._exit(). Або ...
Марк Байєрс

3
@Mark Власне, sys.exit видає звичайний виняток. Але так, все, що змушує процес негайно припинити, означатиме, що більше нічого не виконується.
Сурма

1
@Андимент: Дякую Змінено на os._exit.
Марк Байєрс

Цікаво, чому не можна розміщувати код очищення за винятком випадків, якщо код буде введений, за винятком лише випадків, коли знайдено виняток?
Стівен Яків

2
@Stephen З одного боку, нарешті код запускається, навіть якщо ви повернетесь із блоку спробу. У такому випадку ви не потрапите за винятком пункту.
Сурма

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
Це спрацювало б навіть якби ви не використовували пункт "нарешті", який не відповідає на питання, оскільки ОП хоче знати різницю, хороший приклад викликав би іншу помилку, ніж IOError, щоб показати, що нарешті блок-пункт виконується до того, як виняток передається абоненту.
Реда Дріссі

2
Я не знав, що elseце річ. Корисно знати.
мазунки

8

Кодові блоки не еквівалентні. finallyПункт також буде працювати , якщо run_code1()згенерує виняток, крім TypeError, або якщо run_code2()згенерує виняток, в той час як other_code()в першій версії не буде працювати в цих випадках.


7

У вашому першому прикладі, що станеться, якщо run_code1()виникає виняток, якого немає TypeError? ... other_code()не буде виконано.

Порівняйте це з finally:версією: other_code()гарантовано буде виконано незалежно від будь-якого винятку.


7

Як пояснено в документації , 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пункт був виконаний.

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


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 в значній мірі застосовує використання, нарешті, для очищення будь-яких ресурсів, створених перед блоком спробу, щоб не викликати витік пам'яті. Так також працюють Java, Python та Ruby.

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

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

Щодо того, чому вам насправді потрібен остаточний блок, не всі мови. У C ++, де ви автоматично викликали деструктори, які здійснюють очищення, коли виняток розгортає стек. Я думаю, що це крок у бік більш чистого коду порівняно із спробою ... нарешті, мовами.

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

2

У блоці спробу є лише один обов'язковий пункт: Спробуйте. За винятком інших пунктів і, нарешті, пункти є необов'язковими та засновані на вподобаннях користувача.

нарешті: Перш ніж Python покине оператор спробу, він запустить код в остаточному блоці за будь-яких умов, навіть якщо він закінчує програму. Наприклад, якщо Python зіткнувся з помилкою під час запуску коду в блоці за винятком або в іншому випадку, остаточний блок все одно буде виконуватися перед зупинкою програми.


1
Це неправильно. За винятком заяви обов'язково. - Лукас Азеведо, 1 лютого о 12:04 Це неправильно, оскільки я щойно скомпілював і запустив програму Python 3.5 з блоком спробу, остаточно, без пункту «крім».
Роб Тау

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))

CASE2:

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.