Как использовать в python try-except
Содержание:
- Что такое исключения?
- 8.4. Raising Exceptions¶
- Ошибки и исключения
- Handling an exception
- Ошибка рекурсии (RecursionError)
- Управление исключениями
- 8.3. Обработка исключений
- 8.7. Defining Clean-up Actions¶
- User-Defined Exceptions
- Пример вложенных блоков try-except
- Python Exception Handling Best Practices
- What is An Exception
- try else
- «Голое» исключение
Что такое исключения?
Разработка программы на любом языке довольно часто бывает связана с возникновением различного рода ошибок, препятствующих получению желаемого результата. Как правило, не все они приводят к моментальному завершению программы: некоторые ошибки можно ликвидировать еще на этапе компиляции, другие же способны оставаться незамеченными достаточно долгое время, а третьи и вовсе не зависят от программиста и возникают лишь в определенных ситуациях.
На сегодняшний день принято выделять такие типы ошибок:
- Синтаксические – возникают из-за синтаксических погрешностей кода;
- Логические – проявляются вследствие логических неточностей в алгоритме;
- Исключения – вызваны некорректными действиями пользователя или системы.
Синтаксические ошибки являются следствием несоблюдения общепринятых норм языка, например, пропуска круглой скобки в предполагаемом месте или некорректного написания имени функции. Такие погрешности успешно отлавливаются компилятором, который сразу же сообщает пользователю о проблеме в написанном коде. Следующий пример показывает, что будет, если пропустить символ двойных кавычек при создании строкового литерала:
print("Hello World!)
После попытки запуска выдастся текст ошибки:
File "main.py", line 1 print("Hello World!) ^ SyntaxError: EOL while scanning string literal
Логические ошибки считаются более сложными в выявлении, поскольку не отлавливаются ни на этапе компиляции, ни во время выполнения готовой программы. Обычно они вызваны определенными недостатками в логике написанного алгоритма, из-за чего пользователь не получает желаемого результата. Ниже показана работа функции, которая должна вычислять среднее значение двух чисел, однако она этого не делает из-за неправильной формулы:
def avg(a, b): return a + b / 2 print(avg(10, 20)) 20
Исключения представляют собой еще один вид ошибок, который проявляется в зависимости от наличия обстоятельств, меняющих ход выполнения программы. Примером этому вполне может быть ввод некорректного значения либо отсутствие нужного файла. Как правило, все доступные виды исключений становятся видимыми во время выполнения программы. Далее продемонстрирован пример, показывающий невозможность операции деления на ноль:
print(10 / 0)
После попытки запуска будет выведено:
Traceback (most recent call last): File "main.py", line 1, in <module> print(10 / 0) ZeroDivisionError: division by zero
Как можно заметить из результатов выполнения программы, деление на ноль провоцирует исключительную ситуацию, которая приводит к аварийному завершению работы и выводу ошибки на экран. В данном случае отображается файл, а также номер строки кода, где было выброшено исключение ZeroDivisionError. Ниже приводится краткое описание проблемы.
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:
Ошибки и исключения
В любой, особенно большой, программе могут возникать ошибки, приводящие к ее неработоспособности или к тому, что программа делает не то, что должна. Причин возникновения ошибок много.
Программист может сделать ошибку в употреблении самого языка программирования. Другими словами, выразиться так, как выражаться не положено. Например, начать имя переменной с цифры или забыть поставить двоеточие в заголовке сложной инструкции. Подобные ошибки называют синтаксическими, они нарушают синтаксис и пунктуацию языка. Интерпретатор Питона, встретив ошибочное выражение, не знает как его интерпретировать. Поэтому останавливает выполнение программы и выводит соответствующее сообщение, указав на место возникновения ошибки:
>>> 1a = 10 File "<stdin>", line 1 1a = 10 ^ SyntaxError: invalid syntax
В терминологии языка Python здесь возникло исключение, принадлежащее классу SyntaxError. Согласно документации Python синтаксические ошибки все-таки принято относить к ошибкам, а все остальные – к исключениям. В некоторых языках программирования не используется слово «исключение», а ошибки делят на синтаксические и семантические. Нарушение семантики обычно означает, что, хотя выражения написаны верно с точки зрения синтаксиса языка, программа не работает так, как от нее ожидалось. Для сравнения. Вы можете грамотным русским языком сказать несколько предложений, но по смыслу это будет белиберда, или вас поймут не так, как хотелось бы.
В Python не говорят о семантических ошибках, говорят об исключениях. Их множество. В этом уроке мы рассмотрим некоторые из них, в последующих встретимся с еще несколькими.
Если вы попытаетесь обратиться к переменной, которой не было присвоено значение, что в случае Python означает, что переменная вообще не была объявлена, она не существует, то возникнет исключение NameError.
>>> a = 0 >>> print(a + b) Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'b' is not defined
Последнюю строку сообщения можно перевести как «Ошибка имени: имя ‘b’ не определено».
Если исключение возникает при выполнении кода из файла, то вместо «line 1» будет указана строка, в которой оно возникло, например, «line 24». Вместо «<stdin>» будет указано имя файла, например, «test.py». В данном же случае обозначает стандартный поток ввода. По-умолчанию это поток ввода с клавиатуры. Строка 1 – потому что в интерактивном режиме каждое выражение интерпретируется отдельно, как обособленная программка. Если написать выражение, состоящее из нескольких строк, то линия возникновения ошибки может быть другой:
>>> a = 0 >>> if a == 0: ... print(a) ... print(a + b) ... 0 Traceback (most recent call last): File "<stdin>", line 3, in <module> NameError: name 'b' is not defined
Следующие два исключения, о которых следует упомянуть, и с которыми вы уже могли встретиться в предыдущих уроках, это ValueError и TypeError – ошибка значения и ошибка типа.
>>> int("Hi") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: 'Hi' >>> 8 + "3" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'str'
В примере строку «Hi» нельзя преобразовать к целому числу. Возникает исключение ValueError, потому что функция int() не может преобразовать такое значение.
Число 8 и строка «3» принадлежат разным типам, операнд сложения между которыми не поддерживается. При попытке их сложить возникает исключение TypeError.
Деление на ноль вызывает исключение ZeroDivisionError:
>>> 1/0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero
Handling an exception
If you have some suspicious code that may raise an exception, you can defend your program by placing the suspicious code in a try: block. After the try: block, include an except: statement, followed by a block of code which handles the problem as elegantly as possible.
Syntax
Here is simple syntax of try….except…else blocks −
try: You do your operations here; ...................... except ExceptionI: If there is ExceptionI, then execute this block. except ExceptionII: If there is ExceptionII, then execute this block. ...................... else: If there is no exception then execute this block.
Here are few important points about the above-mentioned syntax −
-
A single try statement can have multiple except statements. This is useful when the try block contains statements that may throw different types of exceptions.
-
You can also provide a generic except clause, which handles any exception.
-
After the except clause(s), you can include an else-clause. The code in the else-block executes if the code in the try: block does not raise an exception.
-
The else-block is a good place for code that does not need the try: block’s protection.
Example
This example opens a file, writes content in the, file and comes out gracefully because there is no problem at all −
#!/usr/bin/python try: fh = open("testfile", "w") fh.write("This is my test file for exception handling!!") except IOError: print "Error: can\'t find file or read data" else: print "Written content in the file successfully" fh.close()
This produces the following result −
Written content in the file successfully
Example
This example tries to open a file where you do not have write permission, so it raises an exception −
#!/usr/bin/python try: fh = open("testfile", "r") fh.write("This is my test file for exception handling!!") except IOError: print "Error: can\'t find file or read data" else: print "Written content in the file successfully"
This produces the following result −
Error: can't find file or read data
Ошибка рекурсии (RecursionError)
Эта ошибка связана со стеком и происходит при вызове функций. Как и предполагает название, ошибка рекурсии возникает, когда внутри друг друга исполняется много методов (один из которых — с бесконечной рекурсией), но это ограничено размером стека.
Все локальные переменные и методы размещаются в стеке. Для каждого вызова метода создается стековый кадр (фрейм), внутрь которого помещаются данные переменной или результат вызова метода. Когда исполнение метода завершается, его элемент удаляется.
Чтобы воспроизвести эту ошибку, определим функцию , которая будет рекурсивной — вызывать сама себя в бесконечном цикле. В результате появится ошибка или ошибка рекурсии, потому что стековый кадр будет заполняться данными метода из каждого вызова, но они не будут освобождаться.
Управление исключениями
Python позволяет создавать пользовательские исключения. Так же рассмотрим логирование программы.
Пользовательские исключения
Как правило, исключения автоматически вызываются в нужных ситуациях, однако в Python присутствует возможность запускать их самостоятельно. Для этого применяется ключевое слово raise. Следом за ним необходимо создать новый объект типа Exception, который потом можно обработать при помощи привычных конструкций try-except, как в данном примере:
print("Program started") try: raise Exception("User Exception!") except Exception as e: print(str(e)) print("Program finished") Program started User Exception! Program finished
Чтобы описать собственный тип исключения, нужно создать новый класс, унаследованный от базового типа Exception. Это позволит запускать особые виды исключений в ситуациях, когда поведение пользователя не соответствует алгоритму программы. В конструкторе Exception указываем текст исключения. После того, как оно сработало и было перехвачено, можно его получить с помощью str.
В следующем коде показан процесс генерации исключения NegativeAge, которое не позволяет пользователю ввести отрицательный возраст. Таким образом, экран выдает соответствующую ошибку:
class NegativeAge(Exception): pass print("Program started") try: age = int(input("Enter your age: ")) if age < 0: raise NegativeAge("Exception: Negative age!") print("Success!") except NegativeAge as e: print(e) print("Program finished") Program started Enter your age: -18 Exception: Negative age! Program finished
Запись в лог
Для вывода специальных сообщений, не влияющих на функционирование программы, в Python применяется библиотека логов. Чтобы воспользоваться ею, необходимо выполнить импорт в верхней части файла. Существует несколько типов логов, отобразить которые поможет команда, определенная во второй строке кода в следующем примере программы:
import logging logging.basicConfig(level = logging.DEBUG) logging.debug("Debug message!") logging.info("Info message!") logging.warning("Warning message!") logging.error("Error message!") logging.critical("Critical message!") DEBUG:root:Debug message! INFO:root:Info message! WARNING:root:Warning message! ERROR:root:Error message! CRITICAL:root:Critical message!
Как видно из результатов выполнения программы, пользователю доступны разные типы сообщений, такие как предупреждение, информация или ошибка. Обычно они применяются при разработке программного обеспечения, чтобы не выводить лишний текст в консоль.
С помощью logging на Python можно записывать в лог и исключения. Обычно лог пишется в файл, зададим его как log.txt. Уровень INFO указывает, что сообщения уровней ниже (в данном случае debug) не будут отражаться в логе. Python позволяет в try-except получить текст ошибки, который и запишем:
import logging logging.basicConfig(filename="log.txt", level = logging.INFO) try: print(10 / 0) except Exception as e: logging.error(str(e))
В log .txt будет добавлена строка сообщения о типе сработавшего исключения “ERROR:root:division by zero”.
8.3. Обработка исключений
Существует возможность писать программы, которые обрабатывают выбранные исключения
Посмотрите на следующий пример, который запрашивает у пользователя ввод до тех пор, пока он не введет допустимое целое число, но позволяет пользователю прервать программу (с помощью Control-C или того, что поддерживает конкретная операционная система); обратите внимание, что сгенерированное пользователем прерывание возникает как исключение KeyboardInterrupt (docs.python.org/3/library/exceptions.html#KeyboardInterrupt) (клавиатурное прерывание)
Оператор try (docs.python.org/3/reference/compound_stmts.html#try) работает следующим образом.
- Сначала выполняется блок try (выражение(я) между ключевыми словами try (docs.python.org/3/reference/compound_stmts.html#try) и except (docs.python.org/3/reference/compound_stmts.html#except)).
- Если исключение не произошло, блок except пропускается и выполнение оператора try закончено.
- Если во время выполнения содержимого try возникает исключение, выражения ниже пропускаются. Затем, если тип возникшего исключения соответствует имени исключения после ключевого слова except, содержимое except выполняется, и затем выполнение продолжается после всего оператора try.
- Если происходит исключение, которое не соответствует имени исключения в строке except, оно передается на внешний оператор try; если обработчик не найден, то исключение становится необработанным и выполнение останавливается с сообщением, как показано выше.
Оператор try может иметь более, чем один пункт except, специальные обработчики для различных исключений. Только один обработчик будет выполнен. Обработчики обрабатывают только те исключения, которые происходят в соответствующей им части try, но не в других обработчиках оператора try. В строке except можно перечислить несколько исключений, взяв их в скобки как кортеж, например:
Класс в блоке except совместим с исключением, если он является таким же классом или базовым классом такового (но не наоборот — блок except, перечисляющий производный класс, несовместим с базовым классом). Например, следующий код выведет B, C, D:
Заметьте, что если бы блоки исключений шли в обратном порядке (первым ), то было бы выведено B, B, B, так как сработало бы первое сопоставление блока except.
В последнем пункте except можно опустить название исключения(ий), он будет служить «джокером»
Используйте эту возможность с особой осторожностью, так как таким образом легко замаскировать действительные ошибки программирования! Такой вариант также может быть использован для вывода сообщения об ошибке, и затем повторной генерации исключения (позволяет вызывающему также обработать исключение):. Оператор try ..
except имеет еще опциональную ветку else, которая если присутствует, должны следовать после всех веток except. Это полезно для кода, который должен быть выполнен, если в ветке try не возникло никакого исключения. Например:
Оператор try … except имеет еще опциональную ветку else, которая если присутствует, должны следовать после всех веток except. Это полезно для кода, который должен быть выполнен, если в ветке try не возникло никакого исключения. Например:
Использование ветки else лучше, чем добавление дополнительного кода в try, потому что помогает избежать случайного перехвата исключения, которое не было сгенерировано кодом, находящимся под «защитой» оператора try … except.
При возникновении исключения с ним может быть связанное значение, также называемое аргументом исключения. Наличие и тип аргумента зависят от типа исключения.
В ветке except после имени исключения можно указать переменную. Переменная привязана к экземпляру исключения с аргументами хранящимися в . Для удобства экземпляр исключения определяет __str__() (docs.python.org/3/reference/datamodel.html#object.__str__), так что аргументы можно вывести сразу, без того, чтобы ссылаться на . Также возможно проиллюстрировать (instantiate) исключение прежде, чем сгенерировать его и добавлять какие-либо атрибуты, как пожелаете.
Если у исключения есть аргументы, они выводятся как последняя часть (‘detail’ — подробность) сообщения для необработанных исключений.
Обработчики исключений не только обрабатывают исключения, которые происходят непосредственно в ветке try, но и если они происходят внутри функций, которые вызываются (даже ненапрямую) в try. Например:
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.
User-Defined Exceptions
Python also allows you to create your own exceptions by deriving classes from the standard built-in exceptions.
Here is an example related to RuntimeError. Here, a class is created that is subclassed from RuntimeError. This is useful when you need to display more specific information when an exception is caught.
In the try block, the user-defined exception is raised and caught in the except block. The variable e is used to create an instance of the class Networkerror.
class Networkerror(RuntimeError): def __init__(self, arg): self.args = arg
So once you defined above class, you can raise the exception as follows −
try: raise Networkerror("Bad hostname") except Networkerror,e: print e.args
Previous Page
Print Page
Next Page
Пример вложенных блоков try-except
У нас могут быть вложенные блоки try-except в Python. В этом случае, если во вложенном блоке try возникает исключение, для его обработки используется вложенный блок except. В случае, если вложенный объект except не может его обработать, для обработки исключения используются внешние блоки except.
x = 10 y = 0 try: print("outer try block") try: print("nested try block") print(x / y) except TypeError as te: print("nested except block") print(te) except ZeroDivisionError as ze: print("outer except block") print(ze)
Вывод:
outer try block nested try block outer except block division by zero
Python Exception Handling Best Practices
- Always try to handle the exception in the code to avoid abnormal termination of the program.
- When creating a custom exception class, suffix its name with “Error”.
- If the except clauses have the same code, try to catch multiple exceptions in a single except block.
- Use finally block to close heavy resources and remove heavy objects.
- Use else block to log successful execution of the code, send notifications, etc.
- Avoid bare except clause as much as possible. If you don’t know about the exceptions, then only use it.
- Create module-specific exception classes for specific scenarios.
- You can catch exceptions in an except block and then raise another exception that is more meaningful.
- Always raise exceptions with meaningful messages.
- Avoid nested try-except blocks because it reduces the readability of the code.
What is An Exception
Some of us may be familiar with exceptions but there may be others who do not have any idea about exceptions. Exceptions are unexpected error situations where applications generally stop working. Sometimes it will not stop working but will not work properly either.
Here is an example of how an exception occurs. Our applications get input from the user and multiple given numbers. But there will be an exception if the user inputs string. The filename is .
When we call our application like below by providing a character like other then a number we will get an exception.
What is An Exception
We also get the exception type. Python provides a different type of exceptions for different situations. In this code the exception type is .
try else
The else clause is executed if and only if no exception is raised. This is different from the finally clause that’s always executed.
12345678 |
try: x = 1except: print('Failed to set x')else: print('No exception occured')finally: print('We always do this') |
Output:
You can catch many types of exceptions this way, where the else clause is executed only if no exception happens.
1234567891011121314 |
try: lunch()except SyntaxError: print('Fix your syntax')except TypeError: print('Oh no! A TypeError has occured')except ValueError: print('A ValueError occured!')except ZeroDivisionError: print('Did by zero?')else: print('No exception')finally: print('Ok then') |
«Голое» исключение
Есть еще один способ поймать ошибку:
Python
try:
1 / 0
except:
print(«You cannot divide by zero!»)
1 |
try 1 except print(«You cannot divide by zero!») |
Но мы его не рекомендуем. На жаргоне Пайтона, это известно как голое исключение, что означает, что будут найдены вообще все исключения. Причина, по которой так делать не рекомендуется, заключается в том, что вы не узнаете, что именно за исключение вы выловите. Когда у вас возникло что-то в духе ZeroDivisionError, вы хотите выявить фрагмент, в котором происходит деление на ноль. В коде, написанном выше, вы не можете указать, что именно вам нужно выявить. Давайте взглянем еще на несколько примеров:
Python
my_dict = {«a»:1, «b»:2, «c»:3}
try:
value = my_dict
except KeyError:
print(«That key does not exist!»)
1 |
my_dict={«a»1,»b»2,»c»3} try value=my_dict»d» exceptKeyError print(«That key does not exist!») |
Python
my_list =
try:
my_list
except IndexError:
print(«That index is not in the list!»)
1 |
my_list=1,2,3,4,5 try my_list6 exceptIndexError print(«That index is not in the list!») |
В первом примере, мы создали словарь из трех элементов. После этого, мы попытались открыть доступ ключу, которого в словаре нет. Так как ключ не в словаре, возникает KeyError, которую мы выявили. Второй пример показывает список, длина которого состоит из пяти объектов. Мы попытались взять седьмой объект из индекса.
Помните, что списки в Пайтоне начинаются с нуля, так что когда вы говорите 6, вы запрашиваете 7. В любом случае, в нашем списке только пять объектов, по этой причине возникает IndexError, которую мы выявили. Вы также можете выявить несколько ошибок за раз при помощи одного оператора. Для этого существует несколько различных способов. Давайте посмотрим:
Python
my_dict = {«a»:1, «b»:2, «c»:3}
try:
value = my_dict
except IndexError:
print(«This index does not exist!»)
except KeyError:
print(«This key is not in the dictionary!»)
except:
print(«Some other error occurred!»)
1 |
my_dict={«a»1,»b»2,»c»3} try value=my_dict»d» exceptIndexError print(«This index does not exist!») exceptKeyError print(«This key is not in the dictionary!») except print(«Some other error occurred!») |
Это самый стандартный способ выявить несколько исключений. Сначала мы попробовали открыть доступ к несуществующему ключу, которого нет в нашем словаре. При помощи try/except мы проверили код на наличие ошибки KeyError, которая находится во втором операторе except
Обратите внимание на то, что в конце кода у нас появилась «голое» исключение. Обычно, это не рекомендуется, но вы, возможно, будете сталкиваться с этим время от времени, так что лучше быть проинформированным об этом
Кстати, также обратите внимание на то, что вам не нужно использовать целый блок кода для обработки нескольких исключений. Обычно, целый блок используется для выявления одного единственного исключения. Изучим второй способ выявления нескольких исключений:
Python
try:
value = my_dict
except (IndexError, KeyError):
print(«An IndexError or KeyError occurred!»)
1 |
try value=my_dict»d» except(IndexError,KeyError) print(«An IndexError or KeyError occurred!») |
Обратите внимание на то, что в данном примере мы помещаем ошибки, которые мы хотим выявить, внутри круглых скобок. Проблема данного метода в том, что трудно сказать какая именно ошибка произошла, так что предыдущий пример, мы рекомендуем больше чем этот
Зачастую, когда происходит ошибка, вам нужно уведомить пользователя, при помощи сообщения.
В зависимости от сложности данной ошибки, вам может понадобиться выйти из программы. Иногда вам может понадобиться выполнить очистку, перед выходом из программы. Например, если вы открыли соединение с базой данных, вам нужно будет закрыть его, перед выходом из программы, или вы можете закончить с открытым соединением. Другой пример – закрытие дескриптора файла, к которому вы обращаетесь. Теперь нам нужно научиться убирать за собой. Это очень просто, если использовать оператор finally.