Обработка исключений python

8.5. Exception Chaining¶

The statement allows an optional which enables
chaining exceptions. For example:

# exc must be exception instance or None.
raise RuntimeError from exc

This can be useful when you are transforming exceptions. For example:

>>> def func():
...     raise IOError
...
>>> try
...     func()
... except IOError as exc
...     raise RuntimeError('Failed to open database') from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in func
OSError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Failed to open database

Exception chaining happens automatically when an exception is raised inside an
or section. Exception chaining can be
disabled by using idiom:

>>> try
...     open('database.sqlite')
... except IOError
...     raise RuntimeError from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError

Создание настраиваемого класса исключения

Мы можем создать собственный класс исключения, расширив класс Exception. Лучше всего создать базовое исключение, а затем наследовать другие классы. Вот несколько примеров.

class EmployeeModuleError(Exception):
    """Base Exception Class for our Employee module"""
    pass


class EmployeeNotFoundError(EmployeeModuleError):
    """Error raised when employee is not found in the database"""

    def __init__(self, emp_id, msg):
        self.employee_id = emp_id
        self.error_message = msg


class EmployeeUpdateError(EmployeeModuleError):
    """Error raised when employee update fails"""

    def __init__(self, emp_id, sql_error_code, sql_error_msg):
        self.employee_id = emp_id
        self.error_message = sql_error_msg
        self.error_code = sql_error_code

По соглашению об именах к имени класса исключения добавляется суффикс «Ошибка».

Аутентификация

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

Одним из примеров API, которые требует аутентификации, является GitHub’s Authenticated User API. Это конечная точка предоставляет информацию о профиле аутентифицированного пользователя. Чтобы сделать запрос к Authenticated User API, вы можете передать свое имя пользователя и пароль в кортеже :

Запрос будет успешно выполнен, если учетные данные, которые вы передали в кортеже верны. Если вы попробуете сделать запрос не указав учетные данные, то получите в ответ от сервера код состояния .

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

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

Здесь ваш пользовательский механизм получает токен, а затем включает этот токен в заголовок вашего запроса.

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

Пока вы думаете о безопасности, давайте рассмотрим работу с SSL-сертификатами в библиотеке .

Основные исключения

Вы уже сталкивались со множеством исключений. Ниже изложен список основных встроенных исключений (определение в документации к Пайтону):

  • Exception – то, на чем фактически строятся все остальные ошибки;
  • AttributeError – возникает, когда ссылка атрибута или присвоение не могут быть выполнены;
  • IOError – возникает в том случае, когда операция I/O (такая как оператор вывода, встроенная функция open() или метод объекта-файла) не может быть выполнена, по связанной с I/O причине: «файл не найден», или «диск заполнен», иными словами.
  • ImportError – возникает, когда оператор import не может найти определение модуля, или когда оператор не может найти имя файла, который должен быть импортирован;
  • IndexError – возникает, когда индекс последовательности находится вне допустимого диапазона;
  • KeyError – возникает, когда ключ сопоставления (dictionary key) не найден в наборе существующих ключей;
  • KeyboardInterrupt – возникает, когда пользователь нажимает клавишу прерывания(обычно Delete или Ctrl+C);
  • NameError – возникает, когда локальное или глобальное имя не найдено;
  • OSError – возникает, когда функция получает связанную с системой ошибку;
  • SyntaxError — возникает, когда синтаксическая ошибка встречается синтаксическим анализатором;
  • TypeError – возникает, когда операция или функция применяется к объекту несоответствующего типа. Связанное значение представляет собой строку, в которой приводятся подробные сведения о несоответствии типов;
  • ValueError – возникает, когда встроенная операция или функция получают аргумент, тип которого правильный, но неправильно значение, и ситуация не может описано более точно, как при возникновении IndexError;
  • ZeroDivisionError – возникает, когда второй аргумент операции division или modulo равен нулю;

Существует много других исключений, но вы вряд ли будете сталкиваться с ними так же часто. В целом, если вы заинтересованы, вы можете узнать больше о них в документации Пайтон.

Raising Exceptions

We can use raise keyword to throw an exception from our code. Some of the possible scenarios are:

  • Function input parameters validation fails
  • Catching an exception and then throwing a custom exception
class ValidationError(Exception):
    pass


def divide(x, y):
    try:
        if type(x) is not int:
            raise TypeError("Unsupported type")
        if type(y) is not int:
            raise TypeError("Unsupported type")
    except TypeError as e:
        print(e)
        raise ValidationError("Invalid type of arguments")

    if y is 0:
        raise ValidationError("We can't divide by 0.")


try:
    divide(10, 0)
except ValidationError as ve:
    print(ve)

try:
    divide(10, "5")
except ValidationError as ve:
    print(ve)

Output:

We can't divide by 0.
Unsupported type
Invalid type of arguments

Exceptions versus Syntax Errors

Syntax errors occur when the parser detects an incorrect statement. Observe the following example:

The arrow indicates where the parser ran into the syntax error. In this example, there was one bracket too many. Remove it and run your code again:

This time, you ran into an exception error. This type of error occurs whenever syntactically correct Python code results in an error. The last line of the message indicated what type of exception error you ran into.

Instead of showing the message , Python details what type of exception error was encountered. In this case, it was a . Python comes with various built-in exceptions as well as the possibility to create self-defined exceptions.

Приложение finally

Оператор try в Python может иметь необязательное предложение finally. Этот пункт выполняется несмотря ни на что и обычно используется для освобождения внешних ресурсов.

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

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

Вот пример файловых операций, чтобы проиллюстрировать это.

try:
   f = open("test.txt",encoding = 'utf-8')
   # perform file operations
finally:
   f.close()

Этот тип конструкции гарантирует, что файл будет закрыт, даже если во время выполнения программы возникает исключение.

Было полезно224
Нет28

476cookie-checkОбработка исключений в Python с использованием try, except и finally

8.3. Handling Exceptions¶

It is possible to write programs that handle selected exceptions. Look at the
following example, which asks the user for input until a valid integer has been
entered, but allows the user to interrupt the program (using Control-C or
whatever the operating system supports); note that a user-generated interruption
is signalled by raising the exception.

>>> while True
...     try
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError
...         print("Oops!  That was no valid number.  Try again...")
...

The statement works as follows.

  • First, the try clause (the statement(s) between the and
    keywords) is executed.

  • If no exception occurs, the except clause is skipped and execution of the
    statement is finished.

  • If an exception occurs during execution of the try clause, the rest of the
    clause is skipped. Then if its type matches the exception named after the
    keyword, the except clause is executed, and then execution
    continues after the statement.

  • If an exception occurs which does not match the exception named in the except
    clause, it is passed on to outer statements; if no handler is
    found, it is an unhandled exception and execution stops with a message as
    shown above.

A statement may have more than one except clause, to specify
handlers for different exceptions. At most one handler will be executed.
Handlers only handle exceptions that occur in the corresponding try clause, not
in other handlers of the same statement. An except clause may
name multiple exceptions as a parenthesized tuple, for example:

... except (RuntimeError, TypeError, NameError):
...     pass

A class in an clause is compatible with an exception if it is
the same class or a base class thereof (but not the other way around — an
except clause listing a derived class is not compatible with a base class). For
example, the following code will print B, C, D in that order:

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in B, C, D]:
    try
        raise cls()
    except D
        print("D")
    except C
        print("C")
    except B
        print("B")

Note that if the except clauses were reversed (with first), it
would have printed B, B, B — the first matching except clause is triggered.

The last except clause may omit the exception name(s), to serve as a wildcard.
Use this with extreme caution, since it is easy to mask a real programming error
in this way! It can also be used to print an error message and then re-raise
the exception (allowing a caller to handle the exception as well):

import sys

try
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err
    print("OS error: {0}".format(err))
except ValueError
    print("Could not convert data to an integer.")
except
    print("Unexpected error:", sys.exc_info()[])
    raise

The … statement has an optional else
clause, which, when present, must follow all except clauses. It is useful for
code that must be executed if the try clause does not raise an exception. For
example:

for arg in sys.argv1:]:
    try
        f = open(arg, 'r')
    except OSError
        print('cannot open', arg)
    else
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

The use of the clause is better than adding additional code to
the clause because it avoids accidentally catching an exception
that wasn’t raised by the code being protected by the …
statement.

When an exception occurs, it may have an associated value, also known as the
exception’s argument. The presence and type of the argument depend on the
exception type.

The except clause may specify a variable after the exception name. The
variable is bound to an exception instance with the arguments stored in
. For convenience, the exception instance defines
so the arguments can be printed directly without having to
reference . One may also instantiate an exception first before
raising it and add any attributes to it as desired.

>>> try
...     raise Exception('spam', 'eggs')
... except Exception as inst
...     print(type(inst))    # the exception instance
...     print(inst.args)     # arguments stored in .args
...     print(inst)          # __str__ allows args to be printed directly,
...                          # but may be overridden in exception subclasses
...     x, y = inst.args     # unpack args
...     print('x =', x)
...     print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

If an exception has arguments, they are printed as the last part (‘detail’) of
the message for unhandled exceptions.

Exception handlers don’t just handle exceptions if they occur immediately in the
try clause, but also if they occur inside functions that are called (even
indirectly) in the try clause. For example:

The try-finally Clause

You can use a finally: block along with a try: block. The finally block is a place to put any code that must execute, whether the try-block
raised an exception or not. The syntax of the try-finally statement is this −

try:
   You do your operations here;
   ......................
   Due to any exception, this may be skipped.
finally:
   This would always be executed.
   ......................

You cannot use else clause as well along with a finally clause.

Example

#!/usr/bin/python

try:
   fh = open("testfile", "w")
   fh.write("This is my test file for exception handling!!")
finally:
   print "Error: can\'t find file or read data"

If you do not have permission to open the file in writing mode, then this will produce the following result −

Error: can't find file or read data

Same example can be written more cleanly as follows −

#!/usr/bin/python

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print "Going to close the file"
      fh.close()
except IOError:
   print "Error: can\'t find file or read data"

When an exception is thrown in the try block, the execution immediately passes to the finally block. After all the statements in the finally block are executed, the exception is raised again and is handled in the except statements if present in the next higher layer of the try-except statement.

Exception hierarchy¶

The class hierarchy for built-in exceptions is:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

8.4. Raising Exceptions¶

The statement allows the programmer to force a specified
exception to occur. For example:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: HiThere

The sole argument to indicates the exception to be raised.
This must be either an exception instance or an exception class (a class that
derives from ). If an exception class is passed, it will
be implicitly instantiated by calling its constructor with no arguments:

raise ValueError  # shorthand for 'raise ValueError()'

If you need to determine whether an exception was raised but don’t intend to
handle it, a simpler form of the statement allows you to
re-raise the exception:

Объект Response

Response — это объект для проверки результатов запроса.

Давайте сделаем тот же запрос, но на этот раз сохраним его в переменную, чтобы мы могли более подробно изучить его атрибуты и поведение:

В этом примере вы захватили значение, возвращаемое значение , которое является экземпляром Response, и сохранили его в переменной response. Название переменной может быть любым.

Код ответа HTTP

Первый кусок данных, который можно получить из ответа — код состояния (он же код ответа HTTP). Код ответа информирует вас о состоянии запроса.

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

Используя вы можете увидеть статус, который вернул вам в ответ сервер:

вернул 200 — это значит, что запрос успешно выполнен и сервер отдал вам запрашиваемые данные.

Иногда эту информацию можно использовать в коде для принятия решений:

Если сервер возвращает 200, то программа выведет , если код ответа 400, то программа выведет .

Requests делает еще один шаг к тому, чтобы сделать это проще. Если вы используете экземпляр Response в условном выражении, то он получит значение , если код ответа между 200 и 400, и False во всех остальных случаях.

Поэтому вы можете сделать проще последний пример, переписав :

Помните, что этот метод не проверяет, что код состояния равен 200.
Причиной этого является то, что ответы с кодом в диапазоне от 200 до 400, такие как и , тоже считаются истинными, так как они дают некоторый обрабатываемый ответ.

Например, статус 204 говорит о том, что запрос был успешным, но в теле ответа нет содержимого.

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

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

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

Теперь вы знаете многое о том, что делать с кодом ответа от сервера. Но когда вы делаете GET-запрос, вы редко заботитесь только об ответе сервера — обычно вы хотите увидеть больше.

Далее вы узнаете как просмотреть фактические данные, которые сервер отправил в теле ответа.

Content

Ответ на Get-запрос, в теле сообщения часто содержит некую ценную информацию, известную как «полезная нагрузка» («Payload»). Используя атрибуты и методы Response, вы можете просматривать payload в разных форматах.

Чтобы увидеть содержимое ответа в байтах, используйте :

Пока дает вам доступ к необработанным байтам полезной нагрузки ответа, вы можете захотеть преобразовать их в строку с использованием кодировки символов UTF-8. Response это сделает за вас, когда вы укажите :

Поскольку для декодирования байтов в строки требуется схема кодирования, Requests будет пытаться угадать кодировку на основе заголовков ответа. Вы можете указать кодировку явно, установив перед указанием :

Если вы посмотрите на ответ, то вы увидите, что на самом деле это последовательный JSON контент. Чтобы получить словарь, вы можете взять строку, которую получили из и десериализовать ее с помощью . Однако, более простой способ сделать это — использовать .

Тип возвращаемого значения — это словарь, поэтому вы можете получить доступ к значениям в объекте по ключу.

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

Заголовки

Заголовки ответа могут дать вам полезную информацию, такую как тип ответа и ограничение по времени, в течение которого необходимо кэшировать ответ.
Чтобы посмотреть заголовки, укажите :

возвращает похожий на словарь объект, позволяющий получить доступ к значениям объекта по ключу. Например, чтобы получить тип содержимого ответа, вы можете получить доступ к Content-Type:

Используя ключ или — вы получите одно и то же значение.

Теперь вы узнали основное о Response. Вы увидели его наиболее используемые атрибуты и методы в действии. Давайте сделаем шаг назад и посмотрим как изменяются ответы при настройке Get-запросов.

Производительность

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

Время ожидания

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

По умолчанию, будет ждать ответа до бесконечности, поэтому вы почти всегда должны указывать время ожидания. Чтобы установить время ожидания, используйте параметр . Тайм-аут может быть целым числом или числом с плавающей запятой, представляющим количество секунд ожидания ответа:

В первом запросе, время ожидания истекает через одну секунду. Во втором — через 3,05 секунд.

Вы также можете передать кортеж тайм-ауту. Первый элемент в кортеже является тайм-аутом соединения (время, которое позволяет установить клиенту соединение с сервером), а второй элемент — время ожидания чтения (время ожидания ответа после того, как клиент установил соединение):

Если запрос устанавливает соединение в течение 2 секунд и получает данные в течение 5 секунд после установки соединения, то ответ будет возвращен. Если время ожидания истекло — функция вызовет исключение :

Ваша программа может перехватить исключение и ответить соответствующим образом.

Объект Session

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

Под этими абстракциями находится класс . Если вам необходимо настроить контроль над выполнением запросов и повысить производительность вашего приложения — вам может потребоваться использовать экземпляр напрямую.

Сеансы используются для сохранения параметров в запросах. Например, если вы хотите использовать одну и ту же аутентификацию для нескольких запросов, вы можете использовать сеанс:

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

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

Максимальное количество попыток

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

Транспортные адаптеры позволяют вам определять набор конфигураций для каждой службы , с которой вы взаимодействуете. Например, вы хотите, чтобы все запросы к https://api.github.com, повторялись по три раза, прежде чем вызовется исключение . Вы должны сконструировать транспортный адаптер, установить его параметр и подключить его к существующему сеансу:

Когда вы монтируете и в — будет придерживаться этой конфигурации в каждом запросе к https://api.github.com.

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

The try-finally Clause

You can use a finally: block along with a try: block. The finally: block is a place to put any code that must execute, whether the try-block raised an exception or not. The syntax of the try-finally statement is this −

try:
   You do your operations here;
   ......................
   Due to any exception, this may be skipped.
finally:
   This would always be executed.
   ......................

Note − You can provide except clause(s), or a finally clause, but not both. You cannot use else clause as well along with a finally clause.

Example

#!/usr/bin/python3

try:
   fh = open("testfile", "w")
   fh.write("This is my test file for exception handling!!")
finally:
   print ("Error: can\'t find file or read data")
   fh.close()

If you do not have permission to open the file in writing mode, then this will produce the following result −

Error: can't find file or read data

Same example can be written more cleanly as follows −

#!/usr/bin/python3

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print ("Going to close the file")
      fh.close()
except IOError:
   print ("Error: can\'t find file or read data")

This produces the following result −

Going to close the file

When an exception is thrown in the try block, the execution immediately passes to the finally block. After all the statements in the finally block are executed, the exception is raised again and is handled in the except statements if present in the next higher layer of the try-except statement.

8.7. Defining Clean-up Actions¶

The statement has another optional clause which is intended to
define clean-up actions that must be executed under all circumstances. For
example:

>>> try
...     raise KeyboardInterrupt
... finally
...     print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>

If a clause is present, the
clause will execute as the last task before the
statement completes. The clause runs whether or
not the statement produces an exception. The following
points discuss more complex cases when an exception occurs:

  • If an exception occurs during execution of the
    clause, the exception may be handled by an
    clause. If the exception is not handled by an
    clause, the exception is re-raised after the
    clause has been executed.

  • An exception could occur during execution of an
    or clause. Again, the exception is re-raised after
    the clause has been executed.

  • If the statement reaches a ,
    or statement, the
    clause will execute just prior to the
    , or
    statement’s execution.

  • If a clause includes a
    statement, the returned value will be the one from the
    clause’s statement, not the
    value from the clause’s
    statement.

For example:

>>> def bool_return():
...     try
...         return True
...     finally
...         return False
...
>>> bool_return()
False

A more complicated example:

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

As you can see, the clause is executed in any event. The
raised by dividing two strings is not handled by the
clause and therefore re-raised after the
clause has been executed.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector