5 Fehlerbehandlungsmuster in PythonBild von Autor | Leinwand

Wenn es um Fehlerbehandlungen geht, lernen wir normalerweise, wie Sie Strive-Besides-Blöcke verwenden. Aber ist das wirklich genug, wenn unsere Codebasis komplexer wird? Ich glaube nicht. Wenn Sie sich ausschließlich auf Strive-Besides stützen, kann es zu sich wiederholenden, überfüllten und schwer zu machtenden Code führen.

In diesem Artikel werde ich Sie durchführen 5 Erweiterte und dennoch praktische Fehlerbehandlungsmuster Dies kann Ihren Code sauberer, zuverlässiger und leichter zu debuggen. Jedes Muster verfügt über ein Beispiel in der Praxis, sodass Sie deutlich sehen können, wo und warum es Sinn macht. Additionally fangen wir an.

1. Fehleraggregation für die Stapelverarbeitung

Bei der Verarbeitung mehrerer Elemente (z. B. in einer Schleife) möchten Sie möglicherweise die Verarbeitung fortsetzen, auch wenn einige Elemente fehlschlagen, dann melden Sie alle Fehler am Ende. Dieses Muster, genannt FehleraggregationVermeiden Sie es, beim ersten Versagen anzuhalten. Dieses Muster eignet sich hervorragend für die Formularvalidierung, Datenimportszenarien oder eine State of affairs, in der Sie umfassende Suggestions zu allen Problemen geben möchten, anstatt beim ersten Fehler zu stoppen.

Beispiel: Verarbeitung einer Liste der Benutzerdatensätze. Fahren Sie weiter, auch wenn einige versagen.

def process_user_record(document, record_number):
    if not document.get("e-mail"):
        elevate ValueError(f"Report #{record_number} failed: Lacking e-mail in document {document}")
    
    # Simulate processing
    print(f"Processed consumer #{record_number}: {document('e-mail')}")

def process_users(information):
    errors = ()
    for index, document in enumerate(information, begin=1):  
        attempt:
            process_user_record(document, index)
        besides ValueError as e:
            errors.append(str(e))
    return errors

customers = (
    {"e-mail": "qasim@instance.com"},
    {"e-mail": ""},
    {"e-mail": "zeenat@instance.com"},
    {"e-mail": ""}
)

errors = process_users(customers)

if errors:
    print("nProcessing accomplished with errors:")
    for error in errors:
        print(f"- {error}")
else:
    print("All information processed efficiently")

Dieser Code schießt über Benutzeraufzeichnungen und verarbeitet jede einzeln. Wenn ein Datensatz eine E -Mail fehlt, erhöht sie einen ValueError, der in der Fehlerliste gefangen und gespeichert wird. Der Prozess wird für alle Aufzeichnungen fortgesetzt, und am Ende werden alle Fehler gemeldet, ohne die gesamte Cost wie diese zu stoppen:

Output:
Processed consumer #1: qasim@instance.com
Processed consumer #3: zeenat@instance.com

Processing accomplished with errors:
- Report #2 failed: Lacking e-mail in document {'e-mail': ''}
- Report #4 failed: Lacking e-mail in document {'e-mail': ''}

2. Kontext -Supervisor -Muster für das Ressourcenmanagement

Wenn Sie mit Ressourcen wie Dateien, Datenbankverbindungen oder Netzwerkhöhlen arbeiten, müssen Sie sicherstellen, dass sie ordnungsgemäß geöffnet und geschlossen sind, selbst wenn ein Fehler auftritt. Kontextmanager, die die With-Anweisung verwenden, verarbeiten dies automatisch und verringern die Wahrscheinlichkeit von Ressourcenlecks im Vergleich zu manuellen Strive-final-Blöcken. Dieses Muster ist besonders hilfreich für E/A -Operationen oder beim Umgang mit externen Systemen.

Beispiel: Nehmen wir an, Sie lesen eine CSV -Datei und möchten sicherstellen, dass sie ordnungsgemäß geschlossen ist, selbst wenn die Verarbeitung der Datei fehlschlägt.

import csv

def read_csv_data(file_path):
    attempt:
        with open(file_path, 'r') as file:
            print(f"Inside 'with': file.closed = {file.closed}")  # Must be False
            reader = csv.reader(file)
            for row in reader:
                if len(row) < 2:
                    elevate ValueError("Invalid row format")
                print(row)
        print(f"After 'with': file.closed = {file.closed}")  # Must be True
        
    besides FileNotFoundError:
        print(f"Error: File {file_path} not discovered")
        print(f"In besides block: file is closed? {file.closed}")

    besides ValueError as e:
        print(f"Error: {e}")
        print(f"In besides block: file is closed? {file.closed}")

# Create check file
with open("information.csv", "w", newline="") as f:
    author = csv.author(f)
    author.writerows((("Title", "Age"), ("Sarwar", "30"), ("Babar"), ("Jamil", "25")))

# Run
read_csv_data("information.csv")

Dieser Code verwendet eine mit Anweisung (Context Supervisor), um die Datei sicher zu öffnen und zu lesen. Wenn eine Zeile weniger als 2 Werte hat, erhöht sie a ValueErroraber die Datei wird immer noch automatisch geschlossen. Der Datei.cloded Überprüfungen bestätigen den Standing der Datei sowohl innerhalb als auch nach dem With Block – selbst bei einem Fehler. Lassen Sie uns den obigen Code ausführen, um dieses Verhalten zu beobachten:

Output:
Inside 'with': file.closed = False
('Title', 'Age')
('Sarwar', '30')
Error: Invalid row format
In besides block: file is closed? True

3.. Ausnahmeverpackung für Kontextfehler

Manchmal liefert eine Ausnahme in einer Funktion auf niedrigerer Ebene nicht genügend Kontext darüber, was in der breiteren Anwendung schief gelaufen ist. Mit Ausnahmeverpackung (oder Verkettung) können Sie eine Ausnahme aufnehmen, einen Kontext hinzufügen und eine neue Ausnahme wieder aufnehmen, die das Unique enthält. Es ist besonders nützlich in geschichteten Anwendungen (z. B. APIs oder Diensten).

Beispiel: Angenommen, Sie holen Benutzerdaten aus einer Datenbank ab und möchten einen Kontext angeben, wenn ein Datenbankfehler auftritt.

class DatabaseAccessError(Exception):
    """Raised when database operations fail."""
    move

def fetch_user(user_id):
    attempt:
        # Simulate database question
        elevate ConnectionError("Failed to hook up with database")
    besides ConnectionError as e:
        elevate DatabaseAccessError(f"Did not fetch consumer {user_id}") from e

attempt:
    fetch_user(123)
besides DatabaseAccessError as e:
    print(f"Error: {e}")
    print(f"Brought on by: {e.__cause__}")

Der ConnectionError wird gefangen und in a eingewickelt Datenbankaccesserror mit zusätzlichem Kontext über die Benutzer -ID. Die aus E -Syntax verbindet die ursprüngliche Ausnahme, sodass die vollständige Fehlerkette zum Debuggen verfügbar ist. Die Ausgabe könnte so aussehen:

Output:
Error: Did not fetch consumer 123
Brought on by: Failed to hook up with database

4. Logik für vorübergehende Fehler wiederholen

Einige Fehler wie Netzwerk -Zeitüberschreitungen oder nicht verfügbare temporäre Dienstleistungen sind vorübergehend und können beim Wiederieren von Wiederholung auflösen. Wenn Sie ein Wiederholungsmuster verwenden, können Sie diese anmutig verarbeiten, ohne Ihren Code mit manuellen Schleifen zu überfüllen. Es automatisiert die Wiederherstellung von vorübergehenden Ausfällen.

Beispiel: Lassen Sie uns einen schuppigen API -Aufruf wiederholen, der gelegentlich aufgrund von simulierten Netzwerkfehlern fehlschlägt. Der folgende Code versucht den API -Aufruf mehrfach mit einer festen Verzögerung zwischen den Wiederholungen. Wenn der Anruf erfolgreich ist, gibt er das Ergebnis sofort zurück. Wenn alle Wiederholungen scheitern, wird eine Ausnahme vom Anrufer behandelt.

import random
import time

def flaky_api_call():
    # Simulate 50% probability of failure (like timeout or server error)
    if random.random() < 0.5:
        elevate ConnectionError("Simulated community failure")
    return {"standing": "success", "information": (1, 2, 3)}

def fetch_data_with_retry(retries=4, delay=2):
    try = 0
    whereas try < retries:
        attempt:
            end result = flaky_api_call()
            print("API name succeeded:", end result)
            return end result
        besides ConnectionError as e:
            try += 1
            print(f"Try {try} failed: {e}. Retrying in {delay} seconds...")
            time.sleep(delay)
    elevate ConnectionError(f"All {retries} makes an attempt failed.")

attempt:
    fetch_data_with_retry()
besides ConnectionError as e:
    print("Closing failure:", e)
Output:
Try 1 failed: Simulated community failure. Retrying in 2 seconds...
API name succeeded: {'standing': 'success', 'information': (1, 2, 3)}

Wie Sie sehen können, ist der erste Versuch aufgrund des simulierten Netzwerkfehlers (der zufällig in 50% der Fälle auftritt) fehl. Die Wiederholungslogik wartete 2 Sekunden und vervollständigte dann den API -Anruf beim nächsten Versuch erfolgreich.

5. benutzerdefinierte Ausnahmeklassen für domänenspezifische Fehler

Anstatt sich auf generische Ausnahmen zu verlassen wie wie ValueError oder RuntimeErrorSie können benutzerdefinierte Ausnahmeklassen erstellen, um bestimmte Fehler in der Domäne Ihrer Anwendung darzustellen. Dies erleichtert semantischer und leichter aufrechterhalten.

Beispiel: Angenommen, ein Zahlungsverarbeitungssystem, bei dem verschiedene Arten von Zahlungsfehlern eine spezifische Behandlung benötigen.

class PaymentError(Exception):
    """Base class for payment-related exceptions."""
    move

class InsufficientFundsError(PaymentError):
    """Raised when the account has inadequate funds."""
    move

class InvalidCardError(PaymentError):
    """Raised when the cardboard particulars are invalid."""
    move

def process_payment(quantity, card_details):
    attempt:
        if quantity > 1000:
            elevate InsufficientFundsError("Not sufficient funds for this transaction")
        if not card_details.get("legitimate"):
            elevate InvalidCardError("Invalid card particulars supplied")
        print("Cost processed efficiently")
    besides InsufficientFundsError as e:
        print(f"Cost failed: {e}")
        # Notify consumer to prime up account
    besides InvalidCardError as e:
        print(f"Cost failed: {e}")
        # Immediate consumer to re-enter card particulars
    besides Exception as e:
        print(f"Surprising error: {e}")
        # Log for debugging

process_payment(1500, {"legitimate": False})

Benutzerdefinierte Ausnahmen (unzureichendesFundSError, InvalidCardError) erben von einer Foundation -PaymentError -Klasse, sodass Sie spezifische Zahlungsprobleme unterschiedlich behandeln können, während Sie unerwartete Fehler mit einem generischen Ausnahmeblock erfassen. Zum Beispiel in der Anruf process_payment (1500, {„gültig“: false})Der erste Scheck löst sich aus, da der Betrag (1500) 1000 überschreitet, sodass er nicht genügend FundSError erhöht. Diese Ausnahme ist im entsprechenden Ausnahme von Block, Druck: Drucken gefangen:

Output:
Cost failed: Not sufficient funds for this transaction

Abschluss

Das warfare’s. In diesem Artikel haben wir 5 praktische Fehlerbehandlungsmuster untersucht:

  1. Fehleraggregation: Verarbeiten Sie alle Elemente, sammeln Sie Fehler und melden Sie sie zusammen
  2. Kontextmanager: Verwalten Sie sicher Ressourcen wie Dateien mit Blöcken
  3. Ausnahmeverpackung: Fügen Sie den Kontext hinzu, indem Sie Ausnahmen fangen und wiederherstellen
  4. Logik wiederholen: Automatisch vorübergehende Fehler wie Netzwerkversagen wiederholen
  5. Benutzerdefinierte Ausnahmen: Erstellen Sie spezifische Fehlerklassen für die klarere Handhabung

Probieren Sie diese Muster in Ihrem nächsten Projekt aus. Mit ein wenig Übung finden Sie, dass Ihr Code leichter beibehalten ist und dass Ihr Fehler viel effektiver wird.

Kanwal Mehreen Kanwal ist ein Ingenieur für maschinelles Lernen und technischer Schriftsteller mit einer tiefgreifenden Leidenschaft für die Datenwissenschaft und die Schnittstelle von KI mit Medizin. Sie hat das eBook „Produktivität mit Chatgpt maximieren“. Als Google -Era -Gelehrte 2022 für APAC setzt sie sich für Vielfalt und akademische Exzellenz ein. Sie wird auch als Teradata -Vielfalt in Tech Scholar, MITACS Globalink Analysis Scholar und Harvard Wecode Scholar anerkannt. Kanwal ist ein leidenschaftlicher Verfechter der Veränderung, nachdem er Femcodes gegründet hat, um Frauen in STEM -Bereichen zu stärken.

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert