Как перехватить несколько исключений в Python
Оглавление
- Как перехватить одно из нескольких возможных исключений Python
- Определите, Какое исключение Python было перехвачено
- Перехватите одно из нескольких возможных исключений Python, используя его Суперкласс
- Игнорируйте несколько исключений Python, используя contextlib.suppress.()
- Перехватывать несколько исключений Python с помощью групп исключений
- Заключение
В этом руководстве вы узнаете о различных методах перехвата нескольких исключений с помощью Python. Для начала вы ознакомитесь с механизмом обработки исключений в Python , прежде чем погрузиться глубже и научиться распознавать то, что вы поймали, иногда игнорировать то, что вы поймали, и даже отлавливать множество исключений.
Python генерирует исключение, когда ваш код сталкивается со случайной, но не непредвиденной ошибкой. Например, это произойдет, если вы попытаетесь прочитать отсутствующий файл. Поскольку вы знаете, что такие исключения могут возникать, вам следует написать код для их устранения или обработки. В отличие от этого, ошибка возникает, когда ваш код выполняет что-то нелогичное, например, ошибку в расчетах. Ошибки следует исправлять, а не обрабатывать. Вот почему важна отладка вашего кода.
Когда ваша программа на Python обнаруживает ошибку и генерирует исключение, ваш код, вероятно, завершит работу с ошибкой, но не раньше, чем в будет отправлено сообщение обратной трассировки, указывающее, в чем проблема:
>>> 12 / "five"
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for /: 'int' and 'str'
Здесь вы попытались разделить число на строку. Python не может этого сделать, поэтому он генерирует TypeError исключение. Затем отображается сообщение обратной трассировки, напоминающее, что оператор деления не работает со строками.
Чтобы вы могли принимать меры при возникновении ошибки, вы реализуете обработку исключений, написав код для перехвата и обработки исключений. Лучше это, чем сбой в вашем коде, который напугает пользователя. Для обработки исключений вы используете оператор try. Это позволяет отслеживать наличие в коде исключений и принимать меры в случае их возникновения.
В большинстве инструкций try используются следующие блоки try ... except:
-
Блок
tryсодержит код, который вы хотите отслеживать на наличие исключений. Любые исключения, возникшие вtry, будут доступны для обработки. -
Один или несколько
exceptблоков, затем следуетtry. Здесь вы определяете код, который будет выполняться при возникновении исключений. В вашем коде любые возникающие исключения запускают соответствующее предложениеexcept. Обратите внимание, что если у вас есть несколько предложенийexcept, ваша программа запустит только первое из них , которое запускает, а затем проигнорирует остальные.
Чтобы узнать, как это работает, вы пишете блок try для отслеживания трех строк кода. Вы включаете два except блока, по одному для ValueError и ZeroDivisionError исключений, чтобы обрабатывать их в случае возникновения:
# handler_statement.py
try:
first = float(input("What is your first number? "))
second = float(input("What is your second number? "))
print(f"{first} divided by {second} is {first / second}")
except ValueError:
print("You must enter a number")
except ZeroDivisionError:
print("You can't divide by zero")
Код, который вы отслеживаете, просит пользователя ввести оба числа, а затем выводит разделение. Если вы не введете число в первых двух строках кода, это приведет к появлению ValueError. Когда функция float() пытается преобразовать ваши входные данные в float, если это невозможно, возникает ValueError. ZeroDivisionError появляется, если вы вводите 0 в качестве второго числа. Когда функция print() пытается разделить на ноль, вы получаете ZeroDivisionError.
Написав приведенный выше код, вы затем тестируете каждый из потоков управления. Чтобы сделать это, вы сначала предоставляете абсолютно достоверные данные, затем указываете string для второго числа и, наконец, указываете 0 для второго числа:
$ python handler_statement.py
What is your first number? 10
What is your second number? 5
10.0 divided by 5.0 is 2.0
$ python handler_statement.py
What is your first number? 10
What is your second number? "five"
You must enter a number
$ python handler_statement.py
What is your first number? 10
What is your second number? 0
You can't divide by zero
Хорошей новостью является то, что ваш код никогда не выходит из строя. Это потому, что ваш код успешно обработал исключения.
Во-первых, вы предоставили приемлемые данные. Если вы посмотрите на выходные данные, то увидите, что программа выполняет только try. Вы не вызвали ни одно из предложений except, потому что Python не вызвал никаких исключений.
Затем вы вызываете ValueError, вводя строку. Это происходит потому, что функция float() не может преобразовать ваш "five" в float. Теперь ваш программный поток становится try, а затем except ValueError. Несмотря на то, что в коде появилось значение ValueError, ваш код исправно справился с этим. Ваши пользователи больше не будут испытывать тревожных сбоев.
В последнем тестовом запуске вы пытаетесь разделить на 0. На этот раз вы вызываете ZeroDivisionError, потому что Python не нравится ваш энтузиазм по поводу сфер Римана и бесконечности. На этот раз последовательность выполнения программы try, затем except ZeroDivisionError. Опять же, ваш код корректно обработал ваше исключение. Большинство ваших пользователей будут довольны этим, хотя математики могут быть разочарованы.
После завершения обработки ошибок выполнение вашей программы обычно продолжается с любым кодом, кроме инструкции try. В данном случае ее нет, поэтому программа просто завершается.
В качестве упражнения вы можете попробовать ввести строку в качестве первого входного сигнала и число в качестве второго. Можете ли вы предсказать, что произойдет, прежде чем попробовать это сделать?
Примечание: Ваш код перехватывает только ZeroDivisionError или ValueError исключения. Если будут вызваны какие-либо другие исключения, произойдет сбой, как и раньше. Вы могли бы обойти это, создав заключительное предложение except Exception для перехвата всех остальных исключений. Однако это плохая практика, поскольку вы можете перехватить исключения, которых не ожидали. Лучше явно перехватывать исключения и настраивать их обработку.
До этого момента вы изучали, как перехватывать исключения по отдельности с помощью инструкции try. В оставшейся части этого руководства вы узнаете о способах перехвата нескольких исключений. Пришло время погрузиться немного глубже.
Получите свой код: Нажмите здесь, чтобы загрузить пример кода, который показывает, как перехватывать множественные исключения в Python.
Как перехватить одно из нескольких возможных исключений Python
Перехват отдельных исключений в отдельных предложениях except - это правильный путь, если вам нужно выполнить различные действия по обработке различных перехватываемых исключений. Если вы обнаружите, что выполняете одни и те же действия в ответ на разные исключения, то вы можете создать более простой и читаемый код, обработав несколько исключений в одном предложении except. Чтобы сделать это, вы указываете исключения в виде кортежа в инструкции except.
Предположим, теперь вам нравится идея о том, что ваш предыдущий код может обрабатывать оба исключения в одной строке. Для этого вы решили переписать свой код следующим образом:
# multiple_exceptions.py
try:
first = float(input("What is your first number? "))
second = float(input("What is your second number? "))
print(f"{first} divided by {second} is {first / second}")
except (ValueError, ZeroDivisionError):
print("There was an error")
На этот раз, если вы обнаружите исключение ValueError или ZeroDivisionError, вы обработаете его с помощью того же предложения except. Вы также могли бы включить дополнительные except условия для других исключений, если бы захотели. Это сработало бы для вас так же, как и раньше.
Затем вы можете протестировать эту версию своего кода, используя те же тестовые примеры, что и раньше:
$ python multiple_exceptions.py
What is your first number? 10
What is your second number? 5
10.0 divided by 5.0 is 2.0
$ python multiple_exceptions.py
What is your first number? 10
What is your second number? "five"
There was an error
$ python multiple_exceptions.py
What is your first number? 10
What is your second number? 0
There was an error
В вашем первом тесте выполняется только ваш блок try, потому что вы не создаете никаких исключений. Ваши второй и третий тестовые примеры вызывают ValueError и ZeroDivisionError соответственно. Вы обнаружили каждое из них в одном и том же предложении except. В обоих случаях выполнение программы выполняется следующим образом: try затем except (ValueError, ZeroDivisionError). Опять же, ваш код выдерживает исключения без сбоев.
Хотя вы довольны тем, что обработчик безопасно обрабатывает оба исключения одним и тем же способом, вам хотелось бы точно знать, какое именно исключение возникает. Далее вы узнаете, как это сделать.
Определите, какое исключение Python было перехвачено
Если вы знакомы с концепциями объектно-ориентированного программирования, то вы знаете, что классы - это шаблоны, которые определяют содержание и возможности объектно-ориентированного программирования. объекты, которые вы создаете, или создаете из них экземпляры. Когда ваш код вызывает исключение Python, он фактически создает экземпляр object из класса, который определяет исключение. Например, когда вы создаете исключение ValueError, вы создаете экземпляр класса ValueError.
Хотя для работы с исключениями вам не нужны глубокие знания объектно-ориентированного программирования, вы должны знать, что разные объекты exception существуют только потому, что они созданы из разных классов.
Вы решили попытаться идентифицировать отдельные исключения, обнаруженные в вашем предыдущем коде:
# exception_identification.py
try:
first = float(input("What is your first number? "))
second = float(input("What is your second number? "))
print(f"{first} divided by {second} is {first / second}")
except (ValueError, ZeroDivisionError) as error:
print(f"A {type(error).__name__} has occurred.")
Здесь вы внесли некоторые коррективы в обработчик. Он по-прежнему перехватывает оба исключения ValueError и ZeroDivisionError, но на этот раз вы присваиваете объект exception переменной с именем. Это error позволит вам проанализировать его.
Чтобы найти класс Exception, вы используете type(). Если вы выведете атрибут .__name__ класса, то увидите, какое именно исключение обработал ваш код. Теперь вы можете узнать, какие исключения произошли:
$ python exception_identification.py
What is your first number? 10
What is your second number? 5
10.0 divided by 4.0 is 2.0
$ python exception_identification.py
What is your first number? 10
What is your second number? "five"
A ValueError has occurred.
$ python exception_identification.py
What is your first number? 10
What is your second number? 0
A ZeroDivisionError has occurred.
Первый тест приносит облегчение, поскольку он доказывает, что ваше обновление все еще работает. Второй и третий тесты приводят к возникновению исключений. Как вы можете видеть, ваш код правильно идентифицировал исключения ValueError и ZeroDivisionError.
Предположим, вы решили улучшить свой код, разрешив умножение. Вы также можете вручную увеличить значение RuntimeError если пользователь запрашивает неподдерживаемую операцию или выполняет что-то неожиданное, например, вводит символ возврата каретки:
# exception_identification_with_structural_pattern_matching.py
from operator import mul, truediv
def calculate(operator, operand1, operand2):
return operator(operand1, operand2)
try:
first = float(input("What is your first number? "))
second = float(input("What is your second number? "))
operation = input("Enter either * or /: ")
if operation == "*":
answer = calculate(mul, first, second)
elif operation == "/":
answer = calculate(truediv, first, second)
else:
raise RuntimeError(f"'{operation}' is an unsupported operation")
except (RuntimeError, ValueError, ZeroDivisionError) as error:
print(f"A {type(error).__name__} has occurred")
match error:
case RuntimeError():
print(f"You have entered an invalid symbol: {error}")
case ValueError():
print(f"You have not entered a number: {error}")
case ZeroDivisionError():
print(f"You can't divide by zero: {error}")
else:
print(f"{first} {operation} {second} = {answer}")
На этот раз вы используете operator модуль mul() и truediv() функции для выполнения умножения и деления. Вы передаете их в свою функцию calculate() вместе с двумя числами. Затем ваша функция calculate() вызывает функцию модуля operator, которую вы ей передали, которая выполняет вычисление. Это работает только в том случае, если вы вводите два числа и либо /, либо * для выполнения операции.
Хотя вы могли бы закодировать calculate() для непосредственного использования оператора * или /, использование функции модуля operator упрощает код функции и обеспечивает расширяемость.
Если пользователь вводит недопустимый оператор, то в вашем коде явно возникает RuntimeError. Как и любое другое исключение, это приведет к сбою кода, если вы его не обработаете.
Предложение except вашего обработчика снова содержит набор исключений, которые вы хотите перехватить. Вы, как и прежде, ссылаетесь на исключение с помощью переменной с именем error. Сначала ваш обработчик выводит имя класса исключений, независимо от того, какое исключение было вызвано. Затем вы используете блок match для вывода сообщения, основанного на конкретном обрабатываемом исключении.
Вы выбираете структурное соответствие шаблону вместо множества elif предложений для аккуратности. Исключение, на которое ссылается ваша переменная error, сравнивается с различными предложениями case. Выполняется только первый соответствующий блок case, остальные игнорируются. Выбранный блок case затем выводит сообщение, за которым следует error. Включение error в вашу f-строку выводит сообщение трассировки исключения по умолчанию для более подробного контекста.
Если предложение try больше не вызывает исключений, то ваша программа игнорирует предложение except и управление переходит к предложению else. Вы используете его здесь, чтобы напечатать результат расчета.
Теперь вы повторно тестируете calculate() с обычными входными данными:
$ python exception_identification_with_structural_pattern_matching.py
What is your first number? 10
What is your second number? 4
Enter either * or /: /
10.0 / 4.0 = 2.5
$ python exception_identification_with_structural_pattern_matching.py
What is your first number? 10
What is your second number? 5
Enter either * or /: *
10.0 * 5.0 = 50.0
Как вы видите, деление по-прежнему работает, как и новая функция умножения. Разверните раздел ниже, чтобы увидеть, что происходит при создании исключений.
В этих тестах ваши исключения не приводят к сбою кода:
$ python exception_identification_with_structural_pattern_matching.py
What is your first number? 10
What is your second number? 5
Enter either * or /: +
A RuntimeError has occurred
You have entered an invalid symbol: '+' is an unsupported operation
$ python exception_identification_with_structural_pattern_matching.py
What is your first number? 10
What is your second number? 5
Enter either * or /:
A RuntimeError has occurred
You have entered an invalid symbol: '' is an unsupported operation
$ python exception_identification_with_structural_pattern_matching.py
What is your first number? 10
What is your second number? "five"
A ValueError has occurred
You have not entered a number: could not convert string to float: '"five"'
$ python exception_identification_with_structural_pattern_matching.py
What is your first number? 10
What is your second number? 0
Enter either * or /: /
A ZeroDivisionError has occurred
You can't divide by zero: float division by zero
Взгляните на последние две строки каждого теста. Ваши обработчики исключений успешно выполнили свою работу.
На этот раз вы выполняете более широкий спектр тестов. Прежде всего, вы пытаетесь выполнить сложение, а затем вам не удается указать операцию. Оба результата равны RuntimeError. Ваши финальные тесты идентичны тем, которые вы выполняли ранее. Как вы можете видеть, ваш код не завершается сбоем и правильно идентифицирует возникшее исключение.
Почему бы не протестировать этот код более полно, чтобы закрепить свое понимание? Возможно, вам также захочется углубиться в изучение модуля operator и использовать его для расширения вашего предыдущего примера, добавив поддержку сложения, вычитания или любых других операций, которые вы пожелаете.
Теперь, когда вы знаете, как идентифицировать различные Exception объекты с помощью их классов, далее вы узнаете, как использовать иерархию исключений в Python , чтобы упростить обработку исключений, обрабатывая вместо этого их родительское исключение.
Перехватить одно из нескольких возможных исключений Python, используя его суперкласс
Вы уже узнали, что различные исключения - это объекты, созданные из разных классов. Все эти классы принадлежат к иерархии классов исключений Python . Все исключения Python наследуются от класса с именем BaseException, и одним из этих подклассов является класс Exception. Это суперкласс всех исключений, о которых вы узнаете в этом руководстве.
В Python содержится более шестидесяти различных исключений. Приведенная ниже диаграмма иллюстрирует лишь некоторые из них, но она содержит все подклассы Exception, которые вы рассмотрите в этом руководстве. Действительно, вот некоторые из распространенных исключений, с которыми вы, вероятно, столкнетесь:
На диаграмме показано, что Exception является суперклассом всех остальных исключений. Подклассы Exception наследуют все, что содержит Exception. Обычно подклассы расширяют свои унаследованные элементы, чтобы дифференцировать себя. В случае исключений наследование в основном сводится к созданию иерархии исключений. Как вы можете видеть, ArithmeticError является подклассом Exception. Внутренние различия между ними незначительны.
Вы также можете увидеть два подкласса класса OSError, а именно PermissionError и FileNotFoundError. Опять же, это означает, что и PermissionError, и FileNotFoundError на самом деле являются подклассами OSError. По сути, оба они также являются Exception подклассами, поскольку OSError наследуется от Exception.
Вы можете использовать тот факт, что подкласс является вариантом своего суперкласса, в своих интересах, чтобы фиксировать различные исключения. Предположим, вы пишете следующее:
# file_fault.py
from os import strerror
try:
with open("datafile.txt", mode="rt") as f:
print(f.readlines())
except OSError as error:
print(strerror(error.errno))
Для начала вы импортируете модуль os, позволяющий вашему коду взаимодействовать с операционной системой. Более конкретно, вы импортируете метод os.strerror(), позволяющий увидеть сообщение об ошибке, связанное с кодом ошибки OSError.
Ваш код выведет содержимое файла с именем datafile.txt, при условии, что он существует. Если datafile.txt не существует, то ваш код выдает FileNotFoundError. Хотя ваше включение только одного предложения except создает впечатление, что вы можете перехватить только OSError, ваш обработчик также может обрабатывать FileNotFoundError, потому что на самом деле это OSError.
Чтобы определить, какой подкласс из OSError вы перехватили, вы можете использовать type(error).__name__ для вывода названия его класса. Однако для большинства пользователей это не имеет смысла. Вместо этого вы можете определить основную ошибку с помощью атрибута .errno. Это число, генерируемое операционной системой, которое предоставляет информацию о проблеме, вызвавшей появление OSError. Само по себе это число не имеет смысла, но связанное с ним сообщение об ошибке расскажет вам больше о проблеме.
Вы написали свой обработчик для использования переменной error, которая ссылается на созданный подкласс OSError. Чтобы увидеть соответствующее сообщение об ошибке, вы вводите код ошибки в функцию os.strerror(). Когда вы распечатаете выходные данные функции, вы узнаете, что именно пошло не так:
$ python file_fault.py
No such file or directory
Как вы можете видеть, файл не существует. Чтобы полностью протестировать код, вам нужно вызвать другие ошибки, например, PermissionError. Возможно, вы захотите попробовать это, создав файл datafile.txt, но убедитесь, что у вас нет прав доступа к нему. Затем попробуйте запустить свой код еще раз и убедитесь, что ваш код распознает и обрабатывает исключение PermissionError.
В качестве упражнения вы могли бы попробовать переписать эту программу, чтобы использовать type(error).__name__ для идентификации конкретных ошибок по имени класса. Кроме того, можете ли вы назвать какие-либо другие исключения, которые этот обработчик также способен перехватывать? Возможно, приведенная выше диаграмма поможет вам найти ответ.
Если вам интересно узнать о ряде конкретных ошибок, с которыми может столкнуться OSErrror, ознакомьтесь с приведенным ниже списком кодов ошибок:
Если вам нужен список кодов ошибок для вашей платформы, вы можете получить его, выполнив этот код:
# error_codes.py
import os
import errno
from pprint import pprint
pprint({e: os.strerror(e) for e in sorted(errno.errorcode)})
Когда вы запустите его, вы увидите полный набор значений кода ошибки, присвоенных OSError.errno :
$ python error_codes.py
{1: 'Operation not permitted',
2: 'No such file or directory',
3: 'No such process',
4: 'Interrupted function call',
5: 'Input/output error',
6: 'No such device or address',
7: 'Arg list too long',
8: 'Exec format error',
9: 'Bad file descriptor',
10: 'No child processes',
11: 'Resource temporarily unavailable',
12: 'Not enough space',
13: 'Permission denied',
14: 'Bad address',
16: 'Resource device',
...
}
В вашем коде отображается полный список кодов ошибок операционной системы, в которой он был запущен, а также связанные с ними сообщения. Как вы можете видеть, эти сообщения дают вам представление о том, что пошло не так.
Потратив некоторое время на изучение того, как обрабатывать исключения, вы теперь будете двигаться дальше, пойдете вразрез с десятым принципом проектирования из Дзен Python и научитесь игнорировать Exceptions. Безумие, как вы могли бы подумать! Ну, не всегда, как вы сейчас увидите.
Игнорировать несколько исключений Python, используя contextlib.suppress()
Обычно, когда ваш код сталкивается с исключительной ситуацией, вы хотите ее обработать. Но иногда вам может потребоваться игнорировать исключения, чтобы ваш код работал. Примерами могут служить проверка сетевой карты на наличие нужных вам данных, которые еще не поступили, или чтение данных из файла, который в данный момент может быть заблокирован другим пользователем.
Традиционный способ игнорировать исключения в Python - это перехватывать их, но ничего с ними не делать. Иногда вы можете увидеть код, подобный этому:
# exception_pass.py
try:
with open("file.txt", mode="rt") as f:
print(f.readlines())
except (FileNotFoundError, PermissionError):
pass
Чтобы игнорировать FileNotFoundError, которое возникает, если этот файл не существует, вы перехватываете исключение обычным способом, а затем используете ключевое слово pass в обработчике, чтобы ничего с ним не делать. Когда вы запускаете свой код на этот раз, выходных данных нет, но ваша программа также не завершает работу:
$ python exception_pass.py
$
Если вы используете этот метод, ваш код может ввести читателей в заблуждение. Вы даете программе команду перехватить исключение, а затем говорите ей, чтобы она не беспокоилась об этом. К счастью, есть более аккуратный способ.
Для написания кода, который явно подавляет исключения, Python предоставляет контекстный менеджер. Чтобы использовать его, вы используете функцию suppress() из модуля contextlib. Вы решили переписать свой код с помощью suppress():
# exception_suppress.py
from contextlib import suppress
with suppress(FileNotFoundError, PermissionError):
with open("file.txt", mode="rt") as f:
print(f.readlines())
Здесь вы используете suppress() для создания контекстного менеджера для подавления как исключений FileNotFoundError, так и исключений PermissionError, а оператор with применяет контекстный менеджер к своему отступу. код. Если бы вы вызвали исключение другого типа, код бы завершился сбоем. Кроме того, если бы вы написали какой-либо код за пределами with, ограничения не были бы применены.
Почему бы не попробовать запустить этот код для несуществующего файла, а затем для файла, к которому у вас нет доступа для чтения? Вы обнаружите, что сбой не происходит.
В качестве другого примера предположим, что в разное время в течение дня создается несколько файлов с именами transactions.txt. Они содержат данные, которые необходимо объединить в файл all_transactions.txt. Затем ваш код заархивирует исходный файл в transactions.arch_ts, где ts - это временная метка для уникальности. Иногда, когда ваша программа ищет файл, его там не оказывается. Вы должны справиться с этим:
# file_archiver.py
import pathlib
import time
from contextlib import suppress
temporary_file = pathlib.Path("transactions.txt")
main_file = pathlib.Path("all_transactions.txt")
while True:
archive_path = temporary_file.with_suffix(f".arch_{time.time()}")
with suppress(FileNotFoundError):
with temporary_file.open(mode="rt") as transactions:
with main_file.open(mode="at") as main:
print("Found new transactions, updating log, & archiving")
main.writelines(transactions.readlines())
temporary_file.replace(archive_path)
time.sleep(3)
Вы решили использовать pathlib, чтобы воспользоваться его возможностями по обработке файлов. Сначала вы создаете два объекта Path, которые представляют пути к двум файлам, используемым вашей программой. Основная часть вашего кода выполняется в бесконечном цикле, но вы используете time.sleep(3) для имитации периодической проверки на наличие файла transactions.txt.
В начале каждой итерации цикла вы используете метод .with_suffix() объекта Path для создания нового Path с расширением .arch_ts. Затем вы подавляете исключение FileNotFoundError. Если это происходит в цикле, вы игнорируете его, чтобы цикл продолжался. Таким образом, ваш код постоянно проверяет наличие transactions.txt, даже если при его отсутствии возникает исключение.
Далее ваш код обрабатывает файл. Вы открываете transactions.txt для чтения в текстовом режиме и all_transactions.txt для добавления в. Именно здесь может возникнуть исключение FileNotFoundError. Если transactions.txt не существует, то исключение FileNotFoundError вернет управление вашим кодом обратно в предложение with suppress(), которое подавит исключение.
Если присутствует transactions.txt, то ваш код открывает или создает файл all_transactions.txt. Затем вы копируете содержимое transactions.txt в конец all_transactions.txt, прежде чем отобразить сообщение для своих пользователей. На этом этапе оба контекстных менеджера завершают работу, автоматически закрывая оба файла. Затем ваш код переименовывает transactions.txt с новым именем с временной меткой.
Независимо от того, существует ли transactions.txt, ваш код будет находиться в спящем режиме в течение трех секунд перед началом следующей итерации цикла, прежде чем повторно проверить, присутствует ли файл в данный момент.
Чтобы попробовать это, загрузите код и три примера файлов с именами transactions1.txt, transactions2.txt, и transactions3.txt в ту же папку. Вы можете получить их по ссылке ниже:
Получите свой код: Нажмите здесь, чтобы загрузить пример кода, который показывает, как перехватывать множественные исключения в Python.
После запуска программы переименуйте transactions1.txt в transactions.txt. Вскоре ваша программа найдет файл и объединит его содержимое в файл all_transactions.txt, прежде чем переименовывать его. Сделайте то же самое для других transaction файлов. Чтобы закрыть программу, используйте Ctrl+C или кнопку stop на вашем Python IDLE.
Вы поймете, что все сработало, когда увидите результат, похожий на следующий:
$ python file_archiver.py
Found new transactions, updating log, & archiving
Found new transactions, updating log, & archiving
Found new transactions, updating log, & archiving
...
После завершения работы ваша программа создаст три архивных файла с уникальными именами. Кроме того, ваш файл all_transactions.txt теперь должен содержать содержимое трех архивных файлов.
Вы также можете заметить, что необработанное исключение KeyBoardInterrupt привело к аварийному завершению работы программы. Это происходит, если вы используете клавиши Ctrl+C для завершения кода. Поскольку вы не перехватили исключение KeyBoardInterrupt, у вашего кода не было другого выбора, кроме как завершить работу. Возможно, вы могли бы попробовать обработать и это исключение.
На данный момент вы изучили несколько способов работы с несколькими возможными исключениями. Однако вам удалось обнаружить только одно из них. В заключительном разделе вы узнаете, как отследить каждого участника в группе исключений.
Перехватывать несколько исключений Python С помощью групп исключений
Когда вы используете try ... except в своем коде, он на самом деле способен перехватывать только первое исключение, возникающее в блоке try. Если вы попытаетесь создать несколько исключений, программа завершит работу после обработки первого исключения. Остальные никогда не будут созданы. Вы можете показать это с помощью кода, подобного следующему:
# catch_first_only.py
exceptions = [ZeroDivisionError(), FileNotFoundError(), NameError()]
num_zd_errors = num_fnf_errors = num_name_errors = 0
try:
for e in exceptions:
raise e
except ZeroDivisionError:
num_zd_errors += 1
except FileNotFoundError:
num_fnf_errors += 1
except NameError:
num_name_errors += 1
finally:
print(f"ZeroDivisionError was raised {num_zd_errors} times.")
print(f"FileNotFoundError was raised {num_fnf_errors} times.")
print(f"NameError was raised {num_name_errors} times.")
Ваша программа начинается с определения списка из трех Exception объектов. Затем вы инициализируете три переменные счетчика нулем. Вы пишете инструкцию try, которая выполняет цикл по вашему списку и пытается вызвать каждое исключение по очереди. Затем вы запускаете код, чтобы точно узнать, что обрабатывается:
$ python catch_first_only.py
ZeroDivisionError was raised 1 times.
FileNotFoundError was raised 0 times.
NameError was raised 0 times.
Как вы можете видеть из выходных данных, ваш код не соответствует требованиям. В то время как исключение ZeroDivisionError вызывается и обрабатывается, ни один из ваших других обработчиков не оказывает никакого влияния. Увеличивается только переменная num_zd_errors.
Вы также использовали в своем коде предложение finally. Это предложение выполняется независимо от того, возникли ли какие-либо исключения. Вы использовали его здесь, чтобы вывести количество каждого возникшего исключения.
Иногда вам может потребоваться обработать все возникающие исключения. Например, это необходимо в параллельном программировании, где ваша программа выполняет несколько задач одновременно. В случаях, когда у вас выполняются параллельные задачи, каждая задача может вызывать свои собственные исключения. Начиная с Python 3.11, язык поддерживает ExceptionGroup объекты и специальное предложение except*, позволяющее обрабатывать все исключения.
Чтобы исследовать группы исключений, вы решили адаптировать свой предыдущий код, чтобы узнать, как вы можете работать с несколькими исключениями. Для этого вы используете специальный синтаксис except*:
# catch_all.py
exceptions = [ZeroDivisionError(), FileNotFoundError(), NameError()]
num_zd_errors = num_fnf_errors = num_name_errors = 0
try:
raise ExceptionGroup("Errors Occurred", exceptions)
except* ZeroDivisionError:
num_zd_errors += 1
except* FileNotFoundError:
num_fnf_errors += 1
except* NameError:
num_name_errors += 1
finally:
print(f"ZeroDivisionError was raised {num_zd_errors} times.")
print(f"FileNotFoundError was raised {num_fnf_errors} times.")
print(f"NameError was raised {num_name_errors} times.")
В этой версии вашего кода ваше предложение try создает новый объект ExceptionGroup, экземпляр которого содержит содержимое вашего списка exceptions. Когда ваша программа вызывает это значение, она передает все исключений обработчикам.
Чтобы приспособиться к вашей группе и убедиться, что все обработчики могут обрабатывать все исключения, а не только первое, обработчики используют except* вместо обычного except. Это означает, что при вызове ExceptionGroup предложение except* ZeroDivisionError обрабатывает любые ZeroDivisionError исключения. Затем ваша программа передает оставшиеся исключения в предложение except* FileNotFoundError и так далее, через каждый обработчик.
Затем вы запускаете его, чтобы точно узнать, что обрабатывается, надеясь, что это больше, чем в прошлый раз:
$ python python catch_all.py
ZeroDivisionError was raised 1 times.
FileNotFoundError was raised 1 times.
NameError was raised 1 times.
Успеха! Как вы можете видеть из выходных данных, ваша программа успешно обработала все ваши исключения и правильно увеличила все три переменные. Для дальнейшего изучения, почему бы не настроить ваш список так, чтобы он содержал несколько экземпляров каждого исключения, и не посмотреть, что произойдет? Затем попробуйте добавить в список несколько исключений, которые вы не включили в обработчик. Что происходит сейчас?
Заключение
Теперь вы знаете, как использовать некоторые из наиболее тонких функций механизма обработки исключений в Python. Вы можете обрабатывать исключения более эффективными способами, чем вы могли себе представить. Вы также понимаете, что иногда игнорирование исключений необходимо для того, чтобы убедиться, что ваш код выполняет то, что вы от него хотите.
После прочтения этого руководства, теперь вы можете:
- Перехват одно из нескольких возможных исключений
- Определите исключение, которое вы перехватили
- Перехватывать исключения, используя их суперкласс
- Игнорируйте исключения и поймите, зачем вы это делаете
- Перехватывать несколько исключений и обрабатывать их все
Вы на верном пути к тому, чтобы стать выдающимся специалистом по обработке исключений! Если у вас есть какие-либо вопросы, не стесняйтесь обращаться к нам, используя раздел комментариев ниже.
Получите свой код: Нажмите здесь, чтобы загрузить пример кода, который показывает, как перехватывать множественные исключения в Python.
<окончание строительства-статус товара-слаг="питон-поймать несколько исключений" класс="БТН-группы Мб-0" данные-АПИ-статьи-закладки-URL-адрес="/по API/В1/статьи/питон-поймать несколько исключений/закладки/" data-api-article-completion-status-url="/api/v1/articles/python-catch-multiple-exceptions/completion_status/"> окончание строительства-статуса> <акция-кнопка "чистое небо" -текст="интересно #питон статья @realpython.com:" в теле письма="проверить это на Python статье:%0А%0AHow, чтобы поймать несколько исключений в Python" написать-тема="питон статьи для вас" в Twitter-текст="интересно #питон статья @realpython:" url="https://realpython.com/python-catch-multiple-exceptions/" url-title="Как перехватывать множественные исключения в Python"> кнопка поделиться>
Back to Top