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

PythonPythonBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

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

Вы также потренируетесь в модификации файла reader.py для корректной обработки ошибок вместо аварийного завершения программы. Этот практический опыт повысит вашу способность писать надежные программы на Python.

Понимание исключений в Python

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

Что такое исключения?

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

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

Изучение текущего кода

Сначала давайте посмотрим на структуру файла reader.py. Этот файл содержит функции, которые используются для чтения и преобразования данных в формате CSV. Чтобы открыть файл в редакторе, нам нужно перейти в соответствующую директорию. Мы будем использовать команду cd в терминале.

cd /home/labex/project

Теперь, когда мы находимся в правильной директории, давайте посмотрим на содержимое файла reader.py. Этот файл содержит несколько важных функций:

  1. convert_csv(): Эта функция принимает строки данных и использует предоставленную функцию преобразования для их преобразования. Она похожа на машину, которая принимает сырье (строки данных) и преобразует его в другой вид в соответствии с определенным рецептом (функцией преобразования).
  2. csv_as_dicts(): Эта функция читает данные в формате CSV и преобразует их в список словарей. Она также выполняет преобразование типов, то есть она гарантирует, что каждый элемент данных в словаре имеет правильный тип, например, строку, целое число или число с плавающей точкой.
  3. read_csv_as_dicts(): Это обертка (wrapper function). Она похожа на менеджера, который вызывает функцию csv_as_dicts() для выполнения задачи.

Демонстрация проблемы

Давайте посмотрим, что происходит, когда код пытается обработать некорректные данные. Мы откроем интерпретатор Python, который похож на игровую площадку, где мы можем интерактивно тестировать наш код на Python. Чтобы открыть интерпретатор Python, мы используем следующую команду в терминале:

python3

После того, как интерпретатор Python открыт, мы попытаемся прочитать файл missing.csv. Этот файл содержит некоторые отсутствующие или некорректные данные. Мы будем использовать функцию read_csv_as_dicts() из файла reader.py для чтения данных.

from reader import read_csv_as_dicts
port = read_csv_as_dicts('missing.csv', types=[str, int, float])

При запуске этого кода вы должны увидеть сообщение об ошибке, похожее на следующее:

Traceback (most recent call last):
  ...
ValueError: invalid literal for int() with base 10: ''

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

Чтобы выйти из интерпретатора Python, введите следующую команду:

exit()

Понимание потока ошибок

Ошибка возникает в функции convert_csv(), конкретно в следующей строке:

return list(map(lambda row: converter(headers, row), rows))

Функция map() применяет функцию converter к каждой строке в списке rows. Функция converter пытается применить типы (str, int, float) к каждой строке. Но когда она встречает строку с отсутствующими данными, преобразование завершается неудачно. Функция map() не имеет встроенного способа обработки исключений. Поэтому, когда возникает исключение, весь процесс аварийно завершается.

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

Реализация обработки исключений

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

Понимание блоков try-except

Python предоставляет мощный способ обработки исключений с использованием блоков try-except. Давайте разберемся, как они работают.

try:
    ## Code that might cause an exception
    result = risky_operation()
except SomeExceptionType as e:
    ## Code that runs if the exception occurs
    handle_exception(e)

В блоке try вы помещаете код, который может вызвать исключение. Исключение - это ошибка, которая возникает во время выполнения программы. Например, если вы пытаетесь разделить число на ноль, Python вызовет исключение ZeroDivisionError. Когда исключение возникает в блоке try, Python останавливает выполнение кода в блоке try и переходит к соответствующему блоку except. Блок except содержит код, который обработает исключение. SomeExceptionType - это тип исключения, которое вы хотите поймать. Вы можете ловить конкретные типы исключений или использовать общий Exception для ловли всех типов исключений. Часть as e позволяет вам получить доступ к объекту исключения, который содержит информацию об ошибке.

Модификация кода

Теперь давайте применим то, что мы узнали о блоках try-except, к функции convert_csv(). Откройте файл reader.py в своем редакторе.

  1. Замените текущую функцию convert_csv() следующим кодом:
def convert_csv(rows, converter, header=True):
    """
    Convert a sequence of rows to an output sequence according to a conversion function.
    """
    if header:
        headers = next(rows)
    else:
        headers = []

    result = []
    for row_idx, row in enumerate(rows, start=1):
        try:
            ## Try to convert the row
            result.append(converter(headers, row))
        except Exception as e:
            ## Print a warning message for bad rows
            print(f"Row {row_idx}: Bad row: {row}")
            continue

    return result

В этой новой реализации:

  • Мы используем цикл for вместо map() для обработки каждой строки. Это дает нам больше контроля над обработкой каждой строки.
  • Мы обернули код преобразования в блок try-except. Это означает, что если при преобразовании строки возникает исключение, программа не аварийно завершится. Вместо этого она перейдёт в блок except.
  • В блоке except мы выводим сообщение об ошибке для некорректных строк. Это помогает нам определить, какие строки имеют проблемы.
  • После вывода сообщения об ошибке мы используем оператор continue, чтобы пропустить текущую строку и продолжить обработку оставшихся строк.

Сохраните файл после внесения этих изменений.

Тестирование ваших изменений

Давайте протестируем ваш модифицированный код с файлом missing.csv. Сначала откройте интерпретатор Python, выполнив следующую команду в терминале:

python3

После того, как вы находитесь в интерпретаторе Python, выполните следующий код:

from reader import read_csv_as_dicts
port = read_csv_as_dicts('missing.csv', types=[str, int, float])
print(f"Number of valid rows processed: {len(port)}")

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

Row 4: Bad row: ['C', '', '53.08']
Row 7: Bad row: ['DIS', '50', 'N/A']
Row 8: Bad row: ['GE', '', '37.23']
Row 13: Bad row: ['INTC', '', '21.84']
Row 17: Bad row: ['MCD', '', '51.11']
Row 19: Bad row: ['MO', '', '70.09']
Row 22: Bad row: ['PFE', '', '26.40']
Row 26: Bad row: ['VZ', '', '42.92']
Number of valid rows processed: 20

Давайте также убедимся, что программа корректно работает с корректными данными. Выполните следующий код в интерпретаторе Python:

valid_port = read_csv_as_dicts('valid.csv', types=[str, int, float])
print(f"Number of valid rows processed: {len(valid_port)}")

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

Number of valid rows processed: 17

Чтобы выйти из интерпретатора Python, выполните следующую команду:

exit()

Теперь ваш код более надежен. Он может элегантно обрабатывать некорректные данные, пропуская плохие строки вместо аварийного завершения. Это делает вашу программу более надежной и удобной для пользователя.

✨ Проверить решение и практиковаться

Реализация логирования

На этом этапе мы сделаем ваш код лучше. Вместо простых сообщений print мы будем использовать модуль logging Python для правильного логирования. Логирование - это отличный способ отслеживать, что делает ваша программа, особенно когда дело доходит до обработки ошибок и понимания потока выполнения кода.

Понимание модуля логирования

Модуль logging в Python предоставляет гибкий способ отправки лог - сообщений из наших приложений. Он намного мощнее, чем просто использование простых инструкций print. Вот, что он может делать:

  1. Различные уровни логирования (DEBUG, INFO, WARNING, ERROR, CRITICAL): Эти уровни помогают нам классифицировать важность сообщений. Например, DEBUG используется для детальной информации, полезной во время разработки, в то время как CRITICAL - для серьезных ошибок, которые могут остановить программу.
  2. Настраиваемый формат вывода: Мы можем решить, как будут выглядеть лог - сообщения, например, добавить временные метки или другую полезную информацию.
  3. Сообщения могут быть направлены на разные выходы (консоль, файлы и т.д.): Мы можем выбрать показывать лог - сообщения на консоли, сохранять их в файл или даже отправлять на удаленный сервер.
  4. Фильтрация логов по серьезности: Мы можем контролировать, какие сообщения мы видим, основываясь на их уровне логирования.

Добавление логирования в reader.py

Теперь давайте изменим ваш код, чтобы использовать модуль логирования. Откройте файл reader.py.

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

import logging

## Set up a logger for this module
logger = logging.getLogger(__name__)

Инструкция import logging импортирует модуль logging, чтобы мы могли использовать его функции. logging.getLogger(__name__) создает логгер для этого конкретного модуля. Использование __name__ гарантирует, что логгер имеет уникальное имя, связанное с модулем.

Далее мы изменим функцию convert_csv() для использования логирования вместо инструкций print. Вот обновленный код:

def convert_csv(rows, converter, header=True):
    """
    Convert a sequence of rows to an output sequence according to a conversion function.
    """
    if header:
        headers = next(rows)
    else:
        headers = []

    result = []
    for row_idx, row in enumerate(rows, start=1):
        try:
            ## Try to convert the row
            result.append(converter(headers, row))
        except Exception as e:
            ## Log a warning message for bad rows
            logger.warning(f"Row {row_idx}: Bad row: {row}")
            ## Log the reason at debug level
            logger.debug(f"Row {row_idx}: Reason: {str(e)}")
            continue

    return result

Основные изменения здесь следующие:

  • Мы заменили print() на logger.warning() для сообщения об ошибке. Таким образом, сообщение логируется с соответствующим уровнем предупреждения, и мы можем контролировать его видимость позже.
  • Мы добавили новое сообщение logger.debug() с деталями об исключении. Это дает нам больше информации о том, что пошло не так, но оно показывается только если уровень логирования установлен на DEBUG или ниже.
  • str(e) преобразует исключение в строку, чтобы мы могли отобразить причину ошибки в лог - сообщении.

После внесения этих изменений сохраните файл.

Тестирование логирования

Давайте протестируем ваш код с включенным логированием. Откройте интерпретатор Python, выполнив следующую команду в терминале:

python3

После того, как вы находитесь в интерпретаторе Python, выполните следующий код:

import logging
import reader

## Configure logging level to see all messages
logging.basicConfig(level=logging.DEBUG)

port = reader.read_csv_as_dicts('missing.csv', types=[str, int, float])
print(f"Number of valid rows processed: {len(port)}")

Здесь мы сначала импортируем модуль logging и наш модуль reader. Затем мы устанавливаем уровень логирования на DEBUG с помощью logging.basicConfig(level = logging.DEBUG). Это означает, что мы увидим все лог - сообщения, включая DEBUG, INFO, WARNING, ERROR и CRITICAL. Затем мы вызываем функцию read_csv_as_dicts из модуля reader и выводим количество обработанных корректных строк.

Вы должны увидеть вывод, похожий на следующий:

WARNING:reader:Row 4: Bad row: ['C', '', '53.08']
DEBUG:reader:Row 4: Reason: invalid literal for int() with base 10: ''
WARNING:reader:Row 7: Bad row: ['DIS', '50', 'N/A']
DEBUG:reader:Row 7: Reason: could not convert string to float: 'N/A'
...
Number of valid rows processed: 20

Обратите внимание, что модуль логирования добавляет префикс к каждому сообщению, показывая уровень логирования (WARNING/DEBUG) и имя модуля.

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

## Reset the logging configuration
import logging
logging.basicConfig(level=logging.WARNING)

port = reader.read_csv_as_dicts('missing.csv', types=[str, int, float])

На этот раз мы устанавливаем уровень логирования на WARNING с помощью logging.basicConfig(level = logging.WARNING). Теперь вы увидите только сообщения WARNING, а сообщения DEBUG будут скрыты:

WARNING:reader:Row 4: Bad row: ['C', '', '53.08']
WARNING:reader:Row 7: Bad row: ['DIS', '50', 'N/A']
...

Это показывает преимущество использования разных уровней логирования. Мы можем контролировать, сколько деталей показывается в логах, не изменяя наш код.

Чтобы выйти из интерпретатора Python, выполните следующую команду:

exit()

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

Резюме

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

Во - вторых, вы узнали о модуле логирования Python и его преимуществах перед инструкциями print. Вы реализовали различные уровни логирования, такие как WARNING и DEBUG, и узнали, как настроить логирование для разных уровней детализации. Эти навыки являются важными для написания надежных приложений на Python, особенно при работе с ошибочными внешними данными, создании автономных приложений или разработке систем, которым нужна диагностическая информация. Теперь вы можете применить эти техники в своих проектах на Python для улучшения надежности и поддерживаемости.