Примеры идут от простого к сложному: сначала — правила обработки исключений (какой except ставить и в каком порядке), затем — работа с путями и файловой системой, и в конце — связка обеих тем в одной программе, как того требует цель урока. Весь вывод в комментариях получен реальным запуском кода в изолированной тестовой папке.
Пример 1. Базовый try/except
Код в try может упасть с ошибкой; если тип ошибки совпадает с указанным в except, программа не падает, а выполняет обработчик и продолжает работу дальше.
# example_01_basic.py
try:
result = 10 / 0
print("Эта строка не выполнится")
except ZeroDivisionError:
print("Ошибка: деление на ноль!")
print("Программа продолжает работу")
# Ошибка: деление на ноль!
# Программа продолжает работу
Что происходит: исключение возникает на строке 10 / 0, поэтому print("Эта строка не выполнится") не выполняется — управление сразу уходит в подходящий except.
Пример 2. Конкретное исключение vs «голый» except
Три варианта одной и той же ошибки показывают, почему «голый» except: — плохая практика: он скрывает тип и текст ошибки, а заодно перехватывает системные сигналы вроде KeyboardInterrupt.
# example_02a_bare_except.py — так делать нельзя
try:
number = int("abc")
except:
print("Что-то пошло не так")
print("Программа продолжает работу, но причина ошибки скрыта")
# Что-то пошло не так
# Программа продолжает работу, но причина ошибки скрыта
# example_02b_broad_exception.py — лучше, но всё ещё расплывчато
try:
number = int("abc")
except Exception as e:
print(f"Ошибка: {type(e).__name__}: {e}")
# Ошибка: ValueError: invalid literal for int() with base 10: 'abc'
# example_02c_specific.py — правильно
try:
number = int("abc")
except ValueError as e:
print(f"Некорректное значение: {e}")
# Некорректное значение: invalid literal for int() with base 10: 'abc'
Что происходит: все три варианта перехватывают одну и ту же ошибку, но только конкретный except ValueError точно говорит читателю кода, какая именно ошибка ожидается. «Голый» except: перехватит вообще всё, включая ошибки, которые вы не планировали обрабатывать (например, опечатку в другой строке того же блока), и молча спрячет их под общим «Что-то пошло не так».
except и общий except Exception ловят абсолютно любую ошибку в блоке try — в том числе ту, которую вы не предвидели. Программа не упадёт, но и не покажет, что именно сломалось; отлаживать такой код приходится вслепую.
Пример 3. Несколько except: порядок проверки важен
Обработчики проверяются сверху вниз, и срабатывает первый подходящий. Поэтому более специфичные исключения (ZeroDivisionError, TypeError) ставят раньше общего Exception — иначе общий обработчик перехватит их первым, и специфичный код никогда не выполнится.
# example_03_order.py
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
print("Деление на ноль")
except TypeError:
print("Неверный тип операндов")
except Exception as e:
print(f"Другая ошибка: {type(e).__name__}: {e}")
print(safe_divide(10, 0))
print(safe_divide(10, "2"))
print(safe_divide(10, 2))
# Деление на ноль
# None
# Неверный тип операндов
# None
# 5.0
Что происходит: для safe_divide(10, 0) сработал except ZeroDivisionError, для safe_divide(10, "2") — except TypeError (нельзя делить int на str), а safe_divide(10, 2) прошёл без ошибок и вернул 5.0. Функция без return в обработчике неявно возвращает None — отсюда два None в выводе.
Пример 4. Один except на несколько типов через кортеж
Если для двух разных ошибок нужна одна и та же обработка, их типы перечисляют в кортеже: except (ValueError, TypeError) as e. Это короче, чем дублировать одинаковый код в двух блоках except.
# example_04_tuple_except.py
def to_int(value):
try:
return int(value)
except (ValueError, TypeError) as e:
print(f"Не удалось преобразовать {value!r}: {type(e).__name__}: {e}")
return None
print(to_int("42"))
print(to_int("abc"))
print(to_int(None))
# 42
# Не удалось преобразовать 'abc': ValueError: invalid literal for int() with base 10: 'abc'
# None
# Не удалось преобразовать None: TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
# None
Что происходит: int("abc") вызывает ValueError (строка не похожа на число), а int(None) — TypeError (значение вообще не строка и не число). Один обработчик except (ValueError, TypeError) ловит оба случая одинаковым образом.
Пример 5. Полная конструкция try/except/else/finally
else выполняется только если в try не было исключения; finally — всегда, независимо от исхода. Пример прогонялся трижды с разным вводом.
# example_05_full.py
try:
number = int(input("Введите число: "))
result = 10 / number
except ValueError:
print("Некорректное значение.")
except ZeroDivisionError:
print("Деление на ноль.")
else:
print(f"Результат: {result}")
finally:
print("Программа завершена.")
# ввод "abc": Некорректное значение. / Программа завершена.
# ввод "0": Деление на ноль. / Программа завершена.
# ввод "5": Результат: 2.0 / Программа завершена.
Что происходит: для "5" исключения не было, поэтому выполнился else с результатом деления; для "abc" и "0" сработали соответствующие except. В любом случае finally напечатал «Программа завершена.» последним.
Пример 6. raise и повторный запрос ввода
raise позволяет вручную сообщить об ошибке там, где Python сам её не увидит — например, отрицательное число синтаксически корректно, но не подходит по смыслу программы. В цикле это удобно сочетать с повторным запросом ввода.
# example_06_raise.py
while True:
try:
number = int(input("Введите положительное число: "))
if number < 0:
raise ValueError("Число не может быть отрицательным")
print(f"Принято: {number}")
break
except ValueError as e:
print(f"Ошибка: {e}. Попробуйте снова.")
# ввод "-3", затем "5":
# Ошибка: Число не может быть отрицательным. Попробуйте снова.
# Принято: 5
Что происходит: при вводе -3 проверка if number < 0 сама возбуждает ValueError, который тут же перехватывает except в этом же блоке — цикл повторяется. При вводе 5 условие не срабатывает, и break завершает цикл.
Пример 7. Логирование ошибок в файл
Вместо print ошибки удобнее записывать через logging — сообщения сохраняются в файл с меткой времени, уровнем и местом возникновения, и не теряются после закрытия консоли.
# example_07_logging.py
import logging
logging.basicConfig(
filename="app.log",
format="%(asctime)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s",
level=logging.ERROR,
)
try:
result = 10 / 0
except ZeroDivisionError as e:
logging.error(f"Ошибка при делении: {e}")
print("Произошла ошибка, подробности в app.log")
# Произошла ошибка, подробности в app.log
# содержимое app.log:
# 2026-08-13 21:07:59,591 - example_07_logging.py - 12 - ERROR - Ошибка при делении: division by zero
Что происходит: logging.error дописал строку в app.log с реальным временем запуска, именем файла, номером строки и текстом ошибки — этот файл можно смотреть после того, как программа уже закрылась.
Пример 8. os.path: проверка и сборка путей, os.makedirs
os.path даёт функции, которые работают одинаково на Windows и Linux — не нужно вручную думать про / или \. os.makedirs(path, exist_ok=True) создаёт сразу все вложенные папки и не падает, если часть из них уже существует.
# example_08_paths.py
import os
filename = "example.txt"
print("Существует:", os.path.exists(filename))
print("Это файл:", os.path.isfile(filename))
print("Абсолютный путь:", os.path.abspath(filename))
print("Имя файла:", os.path.basename("/home/user/docs/file.txt"))
print("Директория:", os.path.dirname("/home/user/docs/file.txt"))
print("Соединённый путь:", os.path.join("folder", "subfolder", "file.txt"))
os.makedirs("archive/2026/reports", exist_ok=True)
print("Папка создана:", os.path.isdir("archive/2026/reports"))
os.makedirs("archive/2026/reports", exist_ok=True) # повторный вызов — без ошибки
print("Повторный вызов не упал")
# Существует: True
# Это файл: True
# Абсолютный путь: C:\...\examples-51\example.txt
# Имя файла: file.txt
# Директория: /home/user/docs
# Соединённый путь: folder\subfolder\file.txt
# Папка создана: True
# Повторный вызов не упал
Что происходит: запуск был на Windows, поэтому os.path.join и os.path.abspath вернули путь с обратными слэшами — на Linux/macOS те же вызовы вернули бы прямые слэши, и код при этом не изменился бы ни на строку.
Пример 9. Абсолютный и относительный путь
os.getcwd() возвращает текущую рабочую директорию — точку отсчёта для всех относительных путей. os.path.abspath превращает относительный путь в абсолютный, добавляя её спереди.
# example_09_abs_vs_rel.py
import os
print("Текущая директория (os.getcwd):", os.getcwd())
relative = "project/notes.txt"
absolute = os.path.abspath(relative)
print("Относительный путь:", relative)
print("Абсолютный путь: ", absolute)
print("Это один и тот же файл:", os.path.exists(relative) == os.path.exists(absolute))
# Текущая директория (os.getcwd): C:\...\examples-51
# Относительный путь: project/notes.txt
# Абсолютный путь: C:\...\examples-51\project\notes.txt
# Это один и тот же файл: True
Что происходит: project/notes.txt и полный путь до него ссылаются на один и тот же файл — os.path.exists подтвердил это для обоих вариантов. Относительный путь короче и переносимее между машинами, но зависит от того, откуда запущен скрипт; абсолютный путь однозначен, но привязан к конкретному диску и структуре папок.
Пример 10. Рекурсивный обход: os.walk
os.walk обходит дерево директорий и на каждом шаге отдаёт текущую папку, вложенные папки и файлы в ней. Тестовая структура: project/notes.txt, project/report.txt, project/broken.txt и project/subdir/log.txt.
# example_10_walk.py
import os
extension = ".txt"
for root, dirs, files in os.walk("project"):
for file in files:
if file.endswith(extension):
full_path = os.path.join(root, file)
print(f"Найден файл: {full_path}")
# Найден файл: project\broken.txt
# Найден файл: project\notes.txt
# Найден файл: project\report.txt
# Найден файл: project\subdir\log.txt
Что происходит: os.walk сам спустился во вложенную папку subdir и нашёл файл там же, что и в корне project — рекурсию писать вручную не нужно. Файл broken.txt тоже найден: os.walk отбирает файлы только по имени, ему не важно, что внутри.
Пример 11. Связка тем: безопасный обход папки с обработкой ошибок чтения
Это та самая связка двух тем урока, которую формулирует цель на странице обзора: project/broken.txt — настоящий бинарный файл с некорректными для UTF-8 байтами. Без try/except попытка прочитать его как текст уронила бы всю программу на первом же «плохом» файле.
# example_11_walk_try.py
import os
report_lines = 0
for root, dirs, files in os.walk("project"):
for file in files:
if not file.endswith(".txt"):
continue
full_path = os.path.join(root, file)
try:
with open(full_path, "r", encoding="utf-8") as f:
content = f.read()
except UnicodeDecodeError as e:
print(f"Пропускаю {full_path}: не удалось прочитать как текст ({e})")
continue
except OSError as e:
print(f"Пропускаю {full_path}: ошибка доступа ({e})")
continue
report_lines += len(content.splitlines())
print(f"Прочитан {full_path}: {len(content.splitlines())} стр.")
print(f"Всего успешно прочитано строк: {report_lines}")
# Пропускаю project\broken.txt: не удалось прочитать как текст ('utf-8' codec can't decode byte 0xff in position 0: invalid start byte)
# Прочитан project\notes.txt: 1 стр.
# Прочитан project\report.txt: 1 стр.
# Прочитан project\subdir\log.txt: 1 стр.
# Всего успешно прочитано строк: 3
Что происходит: os.walk находит все четыре файла, но open(..., encoding="utf-8") не может декодировать broken.txt и выбрасывает UnicodeDecodeError. Обработчик перехватывает именно её, печатает понятное сообщение, пропускает файл через continue — и цикл продолжает работу с оставшимися тремя файлами вместо того, чтобы упасть целиком.
except OSError (например, для файлов без прав на чтение) — расширение сверх исходной лекции; в конкретном тестовом прогоне он не сработал ни разу, но защищает от ошибок доступа к файловой системе, которые UnicodeDecodeError не покрывает. Менеджер контекста with тоже не разбирается в исходной лекции, но остаётся рекомендуемым способом гарантировать закрытие файла.
Пример 12. Аргументы командной строки: sys.argv
sys.argv — список строк, которые передали при запуске скрипта из терминала. Первый элемент, sys.argv[0], — всегда имя самого скрипта.
# example_12_argv.py
import sys
print("Все аргументы:", sys.argv)
if len(sys.argv) != 2:
print("Использование: python example_12_argv.py <имя_файла>")
sys.exit(1)
target = sys.argv[1]
print(f"Ищем файл: {target}")
# запуск "python example_12_argv.py" (без аргументов):
# Все аргументы: ['example_12_argv.py']
# Использование: python example_12_argv.py <имя_файла>
# запуск "python example_12_argv.py data.txt":
# Все аргументы: ['example_12_argv.py', 'data.txt']
# Ищем файл: data.txt
Что происходит: без аргументов список sys.argv содержит только имя скрипта, проверка len(sys.argv) != 2 срабатывает, и sys.exit(1) завершает программу с ненулевым кодом — это сигнал операционной системе, что что-то пошло не так. С аргументом data.txt список содержит два элемента, и программа идёт дальше.
Как запустить в VS Code
- Создайте файл, например
summary_51_examples.py. - Скопируйте код одного из примеров.
- Откройте терминал в VS Code (Ctrl + `).
- Активируйте виртуальное окружение:
venv\Scripts\activate(Windows) илиsource venv/bin/activate(Mac/Linux). - Запустите:
python summary_51_examples.py - Для примеров 8–11 предварительно создайте файлы
example.txt,data.txtи папкуproject/рядом со скриптом; для примера 12 передайте аргумент после имени файла.
Что делать дальше
Если тема исключений подзабылась — вернитесь к уроку 48 «Исключения: обработка ошибок». Если нужна файловая система подробнее — к уроку 50 «Работа с файловой системой». Дальше по программе — урок 52 «File basics». Если что-то из примеров не запустилось — сначала загляните в типичные ошибки.