summaryrefslogtreecommitdiff
path: root/PYTHON/TRY ... EXCEPT.md
blob: 087abe34439ff4f67a4e78099674b3b997715d53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
## исключения и какие они бывают

В Python исключение — это событие, которое возникает при выполнении программы и меняет его нормальный поток. Также это объект, который обозначает различные типы состояний отказа программы и содержит информацию об ошибке:

- тип ошибки;
- состояние программы на момент, когда ошибка возникла;
- описание ошибки.

При этом в Python много встроенных исключений, которые охватывают стандартные типы ошибок:

- `AssertionError` — возникает, когда оператор assert терпит неудачу.
- `AttributeError` — попытка доступа к атрибуту объекта, которого не существует.
- `EOFError` — возникает, когда функция input () достигает конца файла.
- `FileNotFoundError` — программа пытается открыть несуществующий файл.
- `FloatingPointError` — сбой операции с плавающей запятой.
- `ImportError` — неудачная попытка импорта модуля.
- `IndexError` — попытка доступа к элементу списка по недопустимому индексу, например когда индекс выходит за пределы допустимого диапазона.
- `IndentationError` — неправильные отступы в коде.
- `KeyError` — ключ не найден в словаре.
- `KeyboardInterrupt` — пользователь нажал клавишу прерывания.
- `MemoryError` — операции не хватает памяти.
- `NameError` — попытка использовать переменную или функцию, которая не определена.
- `OSError` — ошибка, связанная с операционной системой, например при проблеме с файловой системой.
- `RuntimeError` — общая ошибка, которая не подпадает под другие категории.
- `SyntaxError` — ошибка в синтаксисе кода.
- `TabError` — ошибка смешивания табуляции и пробелов для отступов.
- `TypeError` — ошибка, которая возникает, когда операция или функция применяются к объекту неподходящего типа.
- `ValueError` — возникает, когда функция получает аргумент правильного типа, но некорректного знания.
- `ZeroDivisionError` — ошибка при делении на ноль.

## Обработка исключений по умолчанию

По умолчанию исключения в Python обрабатываются с помощью блоков `try`, `except`, `else` и `finally`. Стандартная структура этой конструкции такая:

```python
try:
    # код, который может вызвать исключение
except SomeException:
    # код для обработки исключения, которое может возникнуть в блоке try
else:
    # код, который выполняется, если исключения не было
finally:
    # код, который выполняется в любом случае
```

Сначала Python выполняет код в блоке `try`. Если в блоке `try` возникает исключение, Python останавливает выполнение кода в этом блоке и начинает искать блок `except`, который соответствует типу возникшего исключения. Если соответствующий блок найден, выполняет код в этом блоке, если нет — исключение передаётся выше по цепочке вызовов (это может привести к завершению программы, если исключение не будет нигде поймано). Если в блоке `try` не возникло исключение, код выполняется в блоке `else` (если он есть). Затем, наконец, выполняется код в блоке `finally`, независимо от того, было исключение или нет.

Блок `else` бывает полезен, когда нам нужно выполнить какой-то код только в том случае, если блок try завершился успешно. Например, когда нам нужно разделить логику обработки ошибок и основную логику, чтобы код был более читаемым и структурированным. Также это может быть полезно, если мы хотим выполнить какую-то операцию только после успешного выполнения блока try, например когда она требует много ресурсов.

Рассмотрим простой пример с операцией деления на 0:

```python
# выполняем попытку деления
try:
    result = 10 / 0
# исключение при делении на 0
except ZeroDivisionError:
    print("Деление на ноль!")
# этот блок не будет выполнен, поскольку возникло исключение
else:
    print("Ошибок нет, результат:", result)
# этот блок выполнится в любом случае
finally:
    print("Блок finally выполнен.")
```

Блок `finally` гарантирует, что критические операции, такие как освобождение ресурсов или закрытие файлов, будут выполнены независимо от того, произошла ошибка или нет.

## Обработка нескольких типов исключений

Для обработки различных типов исключений, которые могут возникнуть в блоке `try`, можно использовать несколько блоков `except`. Это позволит обрабатывать каждое исключение по-своему, в зависимости от типа исключения.

Например, у нас есть операция деления на какое-то число, которое может ввести пользователь, в том числе из заданного списка. Мы можем предусмотреть исключения для ввода, который не может быть преобразован в целое число, для деления на 0 и для случаев, когда пользователь указывает индекс за пределами списка:

```python
try:
    # код, который может вызвать различные исключения
    x = int(input("Введите число: "))
    result = 10 / x
    my_list = [0, 1, 2, 3]
    print(10 / my_list[x])
# это исключение будет вызвано, если пользователь введёт некорректное значение
except ValueError:
    print("Это не число! Пожалуйста, введите корректное число.")
# это исключение будет вызвано, если пользователь введёт 0
except ZeroDivisionError:
    print("Деление на 0 невозможно! Пожалуйста, введите число, отличное от 0.")
# это исключение будет вызвано, если пользователь введёт число за пределами индексов списка my_list
except IndexError:
    print("Индекс выходит за пределы списка! Пожалуйста, введите число от 0 до 3.")
```

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

```python
try:
    # код, который может вызвать различные исключения
    x = int(input("Введите число: "))
    result = 10 / x
    my_list = [0, 1, 2, 3]
    print(10 / my_list[x])
# это исключение будет вызвано, если пользователь введёт некорректное значение, 0 или число за пределами индексов списка my list
except (ValueError, ZeroDivisionError, IndexError) as e:
    print(f"Произошла ошибка: {e}")
```
## Обработка всех типов исключений

Если мы хотим обработать все возможные исключения, можно использовать блок except, не указывая конкретный тип исключения:

```python
try:
    # код, который может вызвать различные исключения
    x = int(input("Введите число: "))
    result = 10 / x
    my_list = [0, 1, 2, 3]
    print(10 / my_list[x])
except Exception as e:
    print(f"Произошла ошибка: {e}")
```

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

## Использование оператора raise

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

Генерация встроенных исключений будет выглядеть так:

```python
def divide(a, b):
    if b == 0:
        # генерируем встроенное исключение
        raise ZeroDivisionError("Деление на ноль невозможно!")
    return a / b

# исключение перехватывается в этом блоке
try:
    result = divide(10, 0)
except ZeroDivisionError as e:
    print(f"Ошибка: {e}")
```

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

```python
# объявляем класс пользовательского исключения
class NegativeNumberError(Exception):
    """Класс для пользовательского исключения при отрицательном числе."""
    pass

def check_positive(number):
    if number < 0:
        # генерируем пользовательское исключение
        raise NegativeNumberError("Число не должно быть отрицательным!")
    return number

try:
    check_positive(-5)
except NegativeNumberError as e:
    print(f"Ошибка: {e}")
```

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

```python
def process_value(value):
    try:
        if value < 0:
            # генерируем исключение
            raise ValueError("Значение не должно быть отрицательным!")
    except ValueError as e:
        print(f"Перехвачено исключение: {e}")
        # повторно генерируем исключение
        raise

try:
    process_value(-10)
except ValueError as e:
    print(f"Повторно перехвачено исключение: {e}")
```
## Наследование от классов исключений

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

```python
class MyCustomError(Exception):
    """Пользовательское исключение для представления специфического типа ошибки."""

    def __init__(self, message):
        self.message = message
        super().__init__(self.message)

try:
    # генерируем пользовательское исключение
    raise MyCustomError("Это моё пользовательское исключение!")
except MyCustomError as e:
    print(f"Поймано пользовательское исключение: {e}")
```
## Логирование исключений

С помощью модуля `logging` бывает полезно вести журнал исключений для обнаружения проблем и отладки приложений. При этом исключения могут быть записаны в файл, выведены в консоль, отправлены на электронную почту и так далее.

```python
import logging

# настраиваем конфигурацию логирования
logging.basicConfig(filename='app.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')

try:
    # код, который может вызывать исключения
    result = 10 / 0
except Exception as e:
    # записываем сообщение об ошибке в журнал
    logging.error("Произошло исключение: %s", e)
```

#==============================================
```python
try:
	print('10' / 0)
except Exception as e:
	print(e)
except ZeroDivisionError as e:
	print(type(e))
	print(e)
except TypeError as e:
	print(type(e))
	print(e)
else: # выполняется если ошибок нет
	print("There was no ERROR !")
finally: # выполняется в любом случае
	print('Continue...')

########################################################

def divide_nums(a, b):
	if b == 0:
		raise ValueError("Second argument can't be 0")
	return a / b

  
try:
	divide_nums(10, 0)
except Exception as e:
	print(isinstance(e, Exception))
	print(e)
	

print('Continue...')

########################################################

def image_info(img):
	if ('image_id' not in img) or ('image_title' not in img):
		raise TypeError("Keys image_id and image_title must be present")
	return f"Image '{img['image_title']}' has id {img['image_id']}"

  
print(image_info({'image_title': 'My cat', 'image_id': 123}))

  
try:
	print(image_info({'image_id': 123}))
except TypeError as e:
	print(e)
```