Ausnahmebehandlung und Protokollierung

PythonPythonBeginner
Jetzt üben

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

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab werden Sie lernen, wie Sie Ausnahmebehandlung (Exception Handling) in Python implementieren. Sie werden verstehen, wie Sie das logging-Modul für eine bessere Fehlerberichterstattung nutzen können, was für das Debugging und die Wartung Ihres Codes von entscheidender Bedeutung ist.

Sie werden auch üben, die Datei reader.py so zu ändern, dass Fehler elegant behandelt werden, anstatt dass das Programm abstürzt. Diese praktische Erfahrung wird Ihre Fähigkeit verbessern, robuste Python-Programme zu schreiben.

Grundlagen zu Ausnahmen (Exceptions) in Python

In diesem Schritt werden wir uns mit Ausnahmen (Exceptions) in Python befassen. Ausnahmen sind ein wichtiges Konzept in der Programmierung. Sie helfen uns, mit unerwarteten Situationen umzugehen, die während der Ausführung eines Programms auftreten können. Wir werden auch herausfinden, warum der aktuelle Code abstürzt, wenn er versucht, ungültige Daten zu verarbeiten. Das Verständnis dieser Zusammenhänge wird Ihnen helfen, robusteres und zuverlässigeres Python-Code zu schreiben.

Was sind Ausnahmen?

In Python sind Ausnahmen Ereignisse, die während der Ausführung eines Programms auftreten und den normalen Ablauf der Anweisungen unterbrechen. Stellen Sie sich das wie eine Baustelle auf einer Autobahn vor. Wenn alles reibungslos verläuft, folgt Ihr Programm einem vorgegebenen Pfad, ähnlich wie ein Auto auf einer freien Straße. Wenn jedoch ein Fehler auftritt, erstellt Python ein Ausnahmeobjekt. Dieses Objekt ist wie ein Bericht, der Informationen darüber enthält, was schief gelaufen ist, wie z. B. der Fehlertyp und der Ort im Code, an dem der Fehler aufgetreten ist.

Wenn diese Ausnahmen nicht richtig behandelt werden, führt dies zum Absturz des Programms. Beim Absturz zeigt Python eine Traceback-Nachricht an. Diese Nachricht ist wie eine Karte, die Ihnen den genauen Ort im Code anzeigt, an dem der Fehler aufgetreten ist. Sie ist sehr nützlich für das Debugging.

Untersuchung des aktuellen Codes

Schauen wir uns zunächst die Struktur der Datei reader.py an. Diese Datei enthält Funktionen, die zum Lesen und Konvertieren von CSV-Daten verwendet werden. Um die Datei im Editor zu öffnen, müssen wir in das richtige Verzeichnis navigieren. Wir verwenden dazu den cd-Befehl im Terminal.

cd /home/labex/project

Jetzt, da wir im richtigen Verzeichnis sind, schauen wir uns den Inhalt von reader.py an. Diese Datei enthält mehrere wichtige Funktionen:

  1. convert_csv(): Diese Funktion nimmt Datenzeilen entgegen und verwendet eine bereitgestellte Konvertierungsfunktion, um sie zu konvertieren. Sie ist wie eine Maschine, die Rohmaterialien (Datenzeilen) nimmt und sie gemäß einem bestimmten Rezept (der Konvertierungsfunktion) in eine andere Form verwandelt.
  2. csv_as_dicts(): Diese Funktion liest CSV-Daten und wandelt sie in eine Liste von Wörterbüchern um. Sie führt auch eine Typkonvertierung durch, was bedeutet, dass sie sicherstellt, dass jedes Datenelement im Wörterbuch den richtigen Typ hat, wie z. B. einen String, eine Ganzzahl oder eine Fließkommazahl.
  3. read_csv_as_dicts(): Dies ist eine Wrapper-Funktion. Sie ist wie ein Manager, der die csv_as_dicts()-Funktion aufruft, um die Aufgabe zu erledigen.

Demonstration des Problems

Schauen wir uns an, was passiert, wenn der Code versucht, ungültige Daten zu verarbeiten. Wir öffnen einen Python-Interpreter, der wie ein Spielplatz ist, auf dem wir unser Python-Code interaktiv testen können. Um den Python-Interpreter zu öffnen, verwenden wir den folgenden Befehl im Terminal:

python3

Sobald der Python-Interpreter geöffnet ist, versuchen wir, die Datei missing.csv zu lesen. Diese Datei enthält einige fehlende oder ungültige Daten. Wir verwenden die read_csv_as_dicts()-Funktion aus der Datei reader.py, um die Daten zu lesen.

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

Wenn Sie diesen Code ausführen, sollten Sie eine Fehlermeldung wie diese sehen:

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

Dieser Fehler tritt auf, weil der Code versucht, einen leeren String in eine Ganzzahl zu konvertieren. Ein leerer String repräsentiert keine gültige Ganzzahl, daher kann Python die Konvertierung nicht durchführen. Die Funktion stürzt beim ersten auftretenden Fehler ab und bricht die Verarbeitung der restlichen gültigen Daten in der Datei ab.

Um den Python-Interpreter zu beenden, geben Sie den folgenden Befehl ein:

exit()

Verständnis des Fehlerablaufs

Der Fehler tritt in der Funktion convert_csv() auf, genauer in der folgenden Zeile:

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

Die map()-Funktion wendet die converter-Funktion auf jede Zeile in der rows-Liste an. Die converter-Funktion versucht, die Typen (str, int, float) auf jede Zeile anzuwenden. Wenn sie jedoch auf eine Zeile mit fehlenden Daten stößt, scheitert sie. Die map()-Funktion hat keine integrierte Möglichkeit, Ausnahmen zu behandeln. Wenn also eine Ausnahme auftritt, stürzt der gesamte Prozess ab.

Im nächsten Schritt werden Sie den Code so ändern, dass diese Ausnahmen elegant behandelt werden. Dies bedeutet, dass das Programm anstatt abzustürzen in der Lage sein wird, mit den Fehlern umzugehen und die Verarbeitung der restlichen Daten fortzusetzen.

Implementierung der Ausnahmebehandlung (Exception Handling)

In diesem Schritt werden wir uns darauf konzentrieren, Ihren Code robuster zu machen. Wenn ein Programm fehlerhafte Daten erhält, stürzt es oft ab. Wir können jedoch eine Technik namens Ausnahmebehandlung (Exception Handling) verwenden, um diese Probleme elegant zu bewältigen. Sie werden die Datei reader.py ändern, um dies zu implementieren. Die Ausnahmebehandlung ermöglicht es Ihrem Programm, auch bei unerwarteten Daten weiterlaufen zu können, anstatt abrupt zu stoppen.

Grundlagen zu Try-Except-Blöcken

Python bietet eine leistungsstarke Möglichkeit, Ausnahmen mit Try-Except-Blöcken zu behandeln. Lassen Sie uns untersuchen, wie diese funktionieren.

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

Im try-Block platzieren Sie den Code, der möglicherweise eine Ausnahme auslöst. Eine Ausnahme ist ein Fehler, der während der Ausführung eines Programms auftritt. Wenn Sie beispielsweise versuchen, eine Zahl durch Null zu teilen, wird Python eine ZeroDivisionError-Ausnahme auslösen. Wenn in einem try-Block eine Ausnahme auftritt, bricht Python die Ausführung des Codes im try-Block ab und springt zum passenden except-Block. Der except-Block enthält den Code, der die Ausnahme behandelt. SomeExceptionType ist der Typ der Ausnahme, die Sie abfangen möchten. Sie können bestimmte Typen von Ausnahmen abfangen oder einen allgemeinen Exception-Typ verwenden, um alle Arten von Ausnahmen abzufangen. Der Teil as e ermöglicht Ihnen den Zugriff auf das Ausnahmeobjekt, das Informationen über den Fehler enthält.

Änderung des Codes

Jetzt wenden wir das, was wir über Try-Except-Blöcke gelernt haben, auf die Funktion convert_csv() an. Öffnen Sie die Datei reader.py in Ihrem Editor.

  1. Ersetzen Sie die aktuelle convert_csv()-Funktion durch den folgenden Code:
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

In dieser neuen Implementierung:

  • Wir verwenden eine for-Schleife anstelle von map(), um jede Zeile zu verarbeiten. Dies gibt uns mehr Kontrolle über die Verarbeitung jeder einzelnen Zeile.
  • Wir umschließen den Konvertierungscode in einen Try-Except-Block. Dies bedeutet, dass wenn während der Konvertierung einer Zeile eine Ausnahme auftritt, das Programm nicht abstürzt. Stattdessen springt es zum except-Block.
  • Im except-Block geben wir eine Fehlermeldung für ungültige Zeilen aus. Dies hilft uns, zu identifizieren, welche Zeilen Probleme haben.
  • Nach dem Ausgeben der Fehlermeldung verwenden wir die continue-Anweisung, um die aktuelle Zeile zu überspringen und die Verarbeitung der verbleibenden Zeilen fortzusetzen.

Speichern Sie die Datei nach diesen Änderungen.

Testen Ihrer Änderungen

Testen wir Ihren geänderten Code mit der Datei missing.csv. Öffnen Sie zunächst den Python-Interpreter, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

python3

Sobald Sie sich im Python-Interpreter befinden, führen Sie den folgenden Code aus:

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)}")

Wenn Sie diesen Code ausführen, sollten Sie Fehlermeldungen für jede problematische Zeile sehen. Das Programm wird jedoch die Verarbeitung fortsetzen und die gültigen Zeilen zurückgeben. Hier ist ein Beispiel für die Ausgabe:

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

Lassen Sie uns auch überprüfen, ob das Programm mit gültigen Daten korrekt funktioniert. Führen Sie den folgenden Code im Python-Interpreter aus:

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

Sie sollten sehen, dass alle Zeilen ohne Fehler verarbeitet werden. Hier ist ein Beispiel für die Ausgabe:

Number of valid rows processed: 17

Um den Python-Interpreter zu beenden, führen Sie den folgenden Befehl aus:

exit()

Jetzt ist Ihr Code robuster. Er kann ungültige Daten elegant behandeln, indem er fehlerhafte Zeilen überspringt, anstatt abzustürzen. Dies macht Ihr Programm zuverlässiger und benutzerfreundlicher.

✨ Lösung prüfen und üben

Implementierung der Protokollierung (Logging)

In diesem Schritt werden wir Ihren Code verbessern. Anstatt einfache print-Nachrichten zu verwenden, nutzen wir das logging-Modul von Python für eine ordnungsgemäße Protokollierung. Die Protokollierung ist eine ausgezeichnete Methode, um zu verfolgen, was Ihr Programm tut, insbesondere wenn es um die Fehlerbehandlung und das Verständnis des Programmablaufs geht.

Grundlagen des Logging-Moduls

Das logging-Modul in Python bietet uns eine flexible Möglichkeit, Protokollnachrichten aus unseren Anwendungen zu senden. Es ist viel leistungsstärker als die einfache Verwendung von print-Anweisungen. Hier ist, was es kann:

  1. Verschiedene Log-Level (DEBUG, INFO, WARNING, ERROR, CRITICAL): Diese Level helfen uns, die Wichtigkeit der Nachrichten zu kategorisieren. Beispielsweise ist DEBUG für detaillierte Informationen gedacht, die während der Entwicklung nützlich sind, während CRITICAL für ernsthafte Fehler steht, die das Programm möglicherweise stoppen können.
  2. Konfigurierbares Ausgabeformat: Wir können entscheiden, wie die Protokollnachrichten aussehen sollen, z. B. indem wir Zeitstempel oder andere nützliche Informationen hinzufügen.
  3. Nachrichten können an verschiedene Ausgaben gerichtet werden (Konsole, Dateien usw.): Wir können auswählen, ob die Protokollnachrichten auf der Konsole angezeigt, in einer Datei gespeichert oder sogar an einen Remote-Server gesendet werden sollen.
  4. Log-Filterung basierend auf der Schweregrad: Wir können steuern, welche Nachrichten wir sehen, basierend auf ihrem Log-Level.

Hinzufügen der Protokollierung zu reader.py

Jetzt ändern wir Ihren Code, um das logging-Modul zu verwenden. Öffnen Sie die Datei reader.py.

Zunächst müssen wir das logging-Modul importieren und einen Logger für dieses Modul einrichten. Fügen Sie den folgenden Code oben in der Datei hinzu:

import logging

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

Die Anweisung import logging importiert das logging-Modul, damit wir seine Funktionen verwenden können. logging.getLogger(__name__) erstellt einen Logger für dieses spezifische Modul. Die Verwendung von __name__ stellt sicher, dass der Logger einen eindeutigen Namen hat, der mit dem Modul verbunden ist.

Als Nächstes ändern wir die Funktion convert_csv(), um die Protokollierung anstelle von print-Anweisungen zu verwenden. Hier ist der aktualisierte Code:

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

Die Hauptänderungen sind:

  • Wir haben print() durch logger.warning() für die Fehlermeldung ersetzt. Auf diese Weise wird die Nachricht mit dem entsprechenden Warn-Level protokolliert, und wir können später ihre Sichtbarkeit steuern.
  • Wir haben eine neue logger.debug()-Nachricht mit Details zur Ausnahme hinzugefügt. Dies gibt uns mehr Informationen darüber, was schief gelaufen ist, aber es wird nur angezeigt, wenn das Log-Level auf DEBUG oder niedriger eingestellt ist.
  • str(e) wandelt die Ausnahme in einen String um, so dass wir den Fehlergrund in der Protokollnachricht anzeigen können.

Nach diesen Änderungen speichern Sie die Datei.

Testen der Protokollierung

Testen wir Ihren Code mit aktivierter Protokollierung. Öffnen Sie den Python-Interpreter, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

python3

Sobald Sie sich im Python-Interpreter befinden, führen Sie den folgenden Code aus:

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)}")

Hier importieren wir zunächst das logging-Modul und unser reader-Modul. Dann setzen wir das Log-Level auf DEBUG mit logging.basicConfig(level=logging.DEBUG). Dies bedeutet, dass wir alle Protokollnachrichten sehen werden, einschließlich DEBUG, INFO, WARNING, ERROR und CRITICAL. Anschließend rufen wir die Funktion read_csv_as_dicts aus dem reader-Modul auf und geben die Anzahl der verarbeiteten gültigen Zeilen aus.

Sie sollten eine Ausgabe wie diese sehen:

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

Beachten Sie, dass das logging-Modul jedem Nachrichten einen Präfix hinzufügt, der das Log-Level (WARNING/DEBUG) und den Modulnamen anzeigt.

Jetzt schauen wir uns an, was passiert, wenn wir das Log-Level so ändern, dass nur Warnungen angezeigt werden. Führen Sie den folgenden Code im Python-Interpreter aus:

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

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

Diesmal setzen wir das Log-Level auf WARNING mit logging.basicConfig(level=logging.WARNING). Jetzt werden Sie nur die WARNING-Nachrichten sehen, und die DEBUG-Nachrichten werden ausgeblendet:

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

Dies zeigt den Vorteil der Verwendung verschiedener Log-Level. Wir können steuern, wie viel Detail in den Protokollen angezeigt wird, ohne unseren Code zu ändern.

Um den Python-Interpreter zu beenden, führen Sie den folgenden Befehl aus:

exit()

Herzlichen Glückwunsch! Sie haben jetzt eine ordnungsgemäße Ausnahmebehandlung und Protokollierung in Ihrem Python-Programm implementiert. Dies macht Ihren Code zuverlässiger und gibt Ihnen bessere Informationen, wenn Fehler auftreten.

Zusammenfassung

In diesem Lab haben Sie mehrere Schlüsselkonzepte zur Ausnahmebehandlung (Exception Handling) und Protokollierung (Logging) in Python gelernt. Zunächst haben Sie verstanden, wie Ausnahmen während der Datenverarbeitung auftreten, und Try-Except-Blöcke implementiert, um sie elegant zu behandeln. Sie haben auch den Code so geändert, dass die Verarbeitung gültiger Daten auch bei Fehlern fortgesetzt wird.

Zweitens haben Sie über das logging-Modul von Python und seine Vorteile gegenüber print-Anweisungen gelernt. Sie haben verschiedene Log-Level wie WARNING und DEBUG implementiert und gesehen, wie Sie die Protokollierung für unterschiedliche Detailgrade konfigurieren können. Diese Fähigkeiten sind von entscheidender Bedeutung für das Schreiben robuster Python-Anwendungen, insbesondere wenn es um fehleranfällige externe Daten geht, unbeaufsichtigte Anwendungen entwickelt werden oder Systeme erstellt werden, die diagnostische Informationen benötigen. Sie können diese Techniken nun in Ihren Python-Projekten anwenden, um eine bessere Zuverlässigkeit und Wartbarkeit zu erreichen.