Rückgabe von Werten aus Funktionen

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 mehrere Werte aus Funktionen in Python zurückgeben können. Sie werden auch optionale Rückgabewerte verstehen und erfahren, wie Sie Fehler effektiv behandeln können.

Darüber hinaus werden Sie das Konzept von Futures (Zukunftswerte) für die nebenläufige Programmierung (concurrent programming) erkunden. Obwohl das Zurückgeben eines Werts einfach erscheinen mag, bieten verschiedene Programmier-Szenarien verschiedene Muster und Überlegungen.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/DataStructuresGroup(["Data Structures"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ErrorandExceptionHandlingGroup(["Error and Exception Handling"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python(("Python")) -.-> python/PythonStandardLibraryGroup(["Python Standard Library"]) python/DataStructuresGroup -.-> python/tuples("Tuples") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("Catching Exceptions") python/AdvancedTopicsGroup -.-> python/threading_multiprocessing("Multithreading and Multiprocessing") python/PythonStandardLibraryGroup -.-> python/data_collections("Data Collections") subgraph Lab Skills python/tuples -.-> lab-132504{{"Rückgabe von Werten aus Funktionen"}} python/function_definition -.-> lab-132504{{"Rückgabe von Werten aus Funktionen"}} python/arguments_return -.-> lab-132504{{"Rückgabe von Werten aus Funktionen"}} python/catching_exceptions -.-> lab-132504{{"Rückgabe von Werten aus Funktionen"}} python/threading_multiprocessing -.-> lab-132504{{"Rückgabe von Werten aus Funktionen"}} python/data_collections -.-> lab-132504{{"Rückgabe von Werten aus Funktionen"}} end

Rückgabe mehrerer Werte aus Funktionen

In Python gibt es eine praktische Lösung, wenn Sie möchten, dass eine Funktion mehr als einen Wert zurückgibt: die Rückgabe eines Tupels. Ein Tupel ist eine Art von Datenstruktur in Python. Es ist eine unveränderliche Sequenz, was bedeutet, dass Sie die Elemente eines Tupels nicht ändern können, sobald es erstellt wurde. Tupel sind nützlich, da sie mehrere Werte unterschiedlicher Typen an einem Ort speichern können.

Erstellen wir nun eine Funktion, um Konfigurationszeilen im Format name=value zu analysieren. Das Ziel dieser Funktion besteht darin, eine Zeile in diesem Format zu nehmen und sowohl den Namen als auch den Wert als separate Elemente zurückzugeben.

  1. Zunächst müssen Sie eine neue Python-Datei erstellen. In dieser Datei werden der Code für unsere Funktion und der Testcode gespeichert. Erstellen Sie im Projektverzeichnis eine Datei mit dem Namen return_values.py. Sie können den folgenden Befehl im Terminal ausführen, um diese Datei zu erstellen:
touch ~/project/return_values.py
  1. Öffnen Sie nun die Datei return_values.py in Ihrem Code-Editor. In dieser Datei schreiben wir die Funktion parse_line. Diese Funktion nimmt eine Zeile als Eingabe, teilt sie am ersten '='-Zeichen auf und gibt den Namen und den Wert als Tupel zurück.
def parse_line(line):
    """
    Parse a line in the format 'name=value' and return both the name and value.

    Args:
        line (str): Input line to parse in the format 'name=value'

    Returns:
        tuple: A tuple containing (name, value)
    """
    parts = line.split('=', 1)  ## Split at the first equals sign
    if len(parts) == 2:
        name = parts[0]
        value = parts[1]
        return (name, value)  ## Return as a tuple

In dieser Funktion wird die Methode split verwendet, um die Eingabezeile am ersten '='-Zeichen in zwei Teile zu teilen. Wenn die Zeile im richtigen name=value-Format vorliegt, extrahieren wir den Namen und den Wert und geben sie als Tupel zurück.

  1. Nachdem die Funktion definiert wurde, müssen wir einige Testcode hinzufügen, um zu überprüfen, ob die Funktion wie erwartet funktioniert. Der Testcode ruft die Funktion parse_line mit einer Beispiel-Eingabe auf und gibt die Ergebnisse aus.
## Test the parse_line function
if __name__ == "__main__":
    result = parse_line('[email protected]')
    print(f"Result as tuple: {result}")

    ## Unpacking the tuple into separate variables
    name, value = parse_line('[email protected]')
    print(f"Unpacked name: {name}")
    print(f"Unpacked value: {value}")

Im Testcode rufen wir zunächst die Funktion parse_line auf und speichern das zurückgegebene Tupel in der Variablen result. Dann geben wir dieses Tupel aus. Anschließend verwenden wir die Tupel-Auf unpacking, um die Elemente des Tupels direkt den Variablen name und value zuzuweisen und sie getrennt auszugeben.

  1. Nachdem Sie die Funktion und den Testcode geschrieben haben, speichern Sie die Datei return_values.py. Öffnen Sie dann das Terminal und führen Sie den folgenden Befehl aus, um das Python-Skript auszuführen:
python ~/project/return_values.py

Sie sollten eine Ausgabe ähnlich der folgenden sehen:

Result as tuple: ('email', '[email protected]')
Unpacked name: email
Unpacked value: [email protected]

Erklärung:

  • Die Funktion parse_line teilt die Eingabezeichenfolge am '='-Zeichen mithilfe der Methode split auf. Diese Methode teilt die Zeichenfolge in Teile auf, basierend auf dem angegebenen Trennzeichen.
  • Sie gibt beide Teile als Tupel zurück, indem sie die Syntax return (name, value) verwendet. Ein Tupel ist eine Möglichkeit, mehrere Werte zusammenzufassen.
  • Wenn Sie die Funktion aufrufen, haben Sie zwei Optionen. Sie können entweder das gesamte Tupel in einer Variablen speichern, wie wir es mit der Variablen result getan haben. Oder Sie können das Tupel direkt in separate Variablen "entpacken" (unpacking), indem Sie die Syntax name, value = parse_line(...) verwenden. Dies erleichtert die Arbeit mit den einzelnen Werten.

Dieses Muster der Rückgabe mehrerer Werte als Tupel ist in Python sehr verbreitet. Es macht Funktionen vielseitiger, da sie mehr als eine Information an den aufrufenden Code liefern können.

Rückgabe optionaler Werte

In der Programmierung gibt es Situationen, in denen eine Funktion möglicherweise kein gültiges Ergebnis generieren kann. Beispielsweise, wenn eine Funktion bestimmte Informationen aus einer Eingabe extrahieren soll, aber die Eingabe nicht das erwartete Format hat. In Python ist es üblich, in solchen Fällen None zurückzugeben. None ist ein spezieller Wert in Python, der die Abwesenheit eines gültigen Rückgabewerts angibt.

Schauen wir uns an, wie wir eine Funktion so modifizieren können, dass sie Fälle behandelt, in denen die Eingabe nicht den erwarteten Kriterien entspricht. Wir werden an der Funktion parse_line arbeiten, die dazu dient, eine Zeile im Format 'name=value' zu analysieren und sowohl den Namen als auch den Wert zurückzugeben.

  1. Aktualisieren Sie die Funktion parse_line in Ihrer Datei return_values.py:
def parse_line(line):
    """
    Parse a line in the format 'name=value' and return both the name and value.
    If the line is not in the correct format, return None.

    Args:
        line (str): Input line to parse in the format 'name=value'

    Returns:
        tuple or None: A tuple containing (name, value) or None if parsing failed
    """
    parts = line.split('=', 1)  ## Split at the first equals sign
    if len(parts) == 2:
        name = parts[0]
        value = parts[1]
        return (name, value)  ## Return as a tuple
    else:
        return None  ## Return None for invalid input

In dieser aktualisierten Funktion parse_line teilen wir zunächst die Eingabezeile am ersten Gleichheitszeichen mithilfe der Methode split auf. Wenn die resultierende Liste genau zwei Elemente enthält, bedeutet dies, dass die Zeile im richtigen 'name=value'-Format vorliegt. Wir extrahieren dann den Namen und den Wert und geben sie als Tupel zurück. Wenn die Liste nicht zwei Elemente enthält, bedeutet dies, dass die Eingabe ungültig ist, und wir geben None zurück.

  1. Fügen Sie Testcode hinzu, um die aktualisierte Funktion zu demonstrieren:
## Test the updated parse_line function
if __name__ == "__main__":
    ## Valid input
    result1 = parse_line('[email protected]')
    print(f"Valid input result: {result1}")

    ## Invalid input
    result2 = parse_line('invalid_line_without_equals_sign')
    print(f"Invalid input result: {result2}")

    ## Checking for None before using the result
    test_line = 'user_info'
    result = parse_line(test_line)
    if result is None:
        print(f"Could not parse the line: '{test_line}'")
    else:
        name, value = result
        print(f"Name: {name}, Value: {value}")

Dieser Testcode ruft die Funktion parse_line sowohl mit gültigen als auch mit ungültigen Eingaben auf und gibt dann die Ergebnisse aus. Beachten Sie, dass wir vor der Verwendung des Ergebnisses der Funktion parse_line zunächst prüfen, ob es None ist. Dies ist wichtig, da wir einen Fehler erhalten würden, wenn wir versuchen würden, einen None-Wert wie ein Tupel zu entpacken.

  1. Speichern Sie die Datei und führen Sie sie aus:
python ~/project/return_values.py

Wenn Sie das Skript ausführen, sollten Sie eine Ausgabe ähnlich der folgenden sehen:

Valid input result: ('email', '[email protected]')
Invalid input result: None
Could not parse the line: 'user_info'

Erklärung:

  • Die Funktion prüft nun, ob die Zeile ein Gleichheitszeichen enthält. Dies geschieht, indem die Zeile am Gleichheitszeichen geteilt und die Länge der resultierenden Liste überprüft wird.
  • Wenn die Zeile kein Gleichheitszeichen enthält, gibt sie None zurück, um anzuzeigen, dass die Analyse fehlgeschlagen ist.
  • Wenn Sie eine solche Funktion verwenden, ist es wichtig, zu prüfen, ob das Ergebnis None ist, bevor Sie es verwenden. Andernfalls können Sie Fehler erhalten, wenn Sie versuchen, auf Elemente eines None-Werts zuzugreifen.

Entwurfsdiskussion:
Ein alternatives Vorgehen zur Behandlung ungültiger Eingaben besteht darin, eine Ausnahme (Exception) auszulösen. Dieser Ansatz ist in bestimmten Situationen geeignet:

  1. Die ungültige Eingabe ist wirklich außergewöhnlich und kein erwarteter Fall. Beispielsweise, wenn die Eingabe von einer vertrauenswürdigen Quelle stammen soll und immer im richtigen Format sein sollte.
  2. Sie möchten zwingen, dass der Aufrufer den Fehler behandelt. Indem Sie eine Ausnahme auslösen, wird der normale Programmablauf unterbrochen, und der Aufrufer muss den Fehler explizit behandeln.
  3. Sie müssen detaillierte Fehlerinformationen bereitstellen. Ausnahmen können zusätzliche Informationen über den Fehler enthalten, was für die Fehlersuche nützlich sein kann.

Beispiel für einen auf Ausnahmen basierten Ansatz:

def parse_line_with_exception(line):
    """Parse a line and raise an exception for invalid input."""
    parts = line.split('=', 1)
    if len(parts) != 2:
        raise ValueError(f"Invalid format: '{line}' does not contain '='")
    return (parts[0], parts[1])

Die Entscheidung zwischen der Rückgabe von None und dem Auslösen von Ausnahmen hängt von den Anforderungen Ihrer Anwendung ab:

  • Geben Sie None zurück, wenn das Fehlen eines Ergebnisses häufig und erwartet ist. Beispielsweise, wenn Sie nach einem Element in einer Liste suchen und es möglicherweise nicht vorhanden ist.
  • Lösen Sie Ausnahmen aus, wenn der Fehler unerwartet ist und den normalen Programmablauf unterbrechen sollte. Beispielsweise, wenn Sie versuchen, auf eine Datei zuzugreifen, die immer vorhanden sein sollte.

Arbeiten mit Futures für parallele Programmierung

In Python bietet die Sprache nützliche Werkzeuge wie Threads und Prozesse, wenn Sie Funktionen gleichzeitig oder parallel ausführen müssen. Hier aber liegt ein häufiges Problem: Wie können Sie den Wert erhalten, den eine Funktion zurückgibt, wenn sie in einem anderen Thread läuft? Hier kommt das Konzept eines Future (Zukunftswert) ins Spiel.

Ein Future ist wie ein Platzhalter für ein Ergebnis, das später verfügbar sein wird. Es ist eine Möglichkeit, einen Wert darzustellen, den eine Funktion in Zukunft produzieren wird, noch bevor die Funktion fertig gelaufen ist. Lassen Sie uns dieses Konzept anhand eines einfachen Beispiels besser verstehen.

Schritt 1: Erstellen einer neuen Datei

Zunächst müssen Sie eine neue Python - Datei erstellen. Wir nennen sie futures_demo.py. Sie können den folgenden Befehl in Ihrem Terminal ausführen, um diese Datei zu erstellen:

touch ~/project/futures_demo.py

Schritt 2: Hinzufügen des grundlegenden Funktionscodes

Öffnen Sie nun die Datei futures_demo.py und fügen Sie den folgenden Python - Code hinzu. Dieser Code definiert eine einfache Funktion und zeigt, wie ein normaler Funktionsaufruf funktioniert.

import time
import threading
from concurrent.futures import Future, ThreadPoolExecutor

def worker(x, y):
    """A function that takes time to complete"""
    print('Starting work...')
    time.sleep(5)  ## Simulate a time-consuming task
    print('Work completed')
    return x + y

## Part 1: Normal function call
print("--- Part 1: Normal function call ---")
result = worker(2, 3)
print(f"Result: {result}")

In diesem Code nimmt die Funktion worker zwei Zahlen, addiert sie, aber simuliert zuerst eine zeitintensive Aufgabe, indem sie für 5 Sekunden anhält. Wenn Sie diese Funktion auf normale Weise aufrufen, wartet das Programm, bis die Funktion fertig ist, und erhält dann den Rückgabewert.

Schritt 3: Ausführen des grundlegenden Codes

Speichern Sie die Datei und führen Sie sie mit dem folgenden Befehl in Ihrem Terminal aus:

python ~/project/futures_demo.py

Sie sollten eine Ausgabe wie die folgende sehen:

--- Part 1: Normal function call ---
Starting work...
Work completed
Result: 5

Dies zeigt, dass ein normaler Funktionsaufruf wartet, bis die Funktion fertig ist, und dann das Ergebnis zurückgibt.

Schritt 4: Ausführen der Funktion in einem separaten Thread

Schauen wir uns nun an, was passiert, wenn wir die Funktion worker in einem separaten Thread ausführen. Fügen Sie den folgenden Code zur Datei futures_demo.py hinzu:

## Part 2: Running in a separate thread (problem: no way to get result)
print("\n--- Part 2: Running in a separate thread ---")
t = threading.Thread(target=worker, args=(2, 3))
t.start()
print("Main thread continues while worker runs...")
t.join()  ## Wait for the thread to complete
print("Worker thread finished, but we don't have its return value!")

Hier verwenden wir die Klasse threading.Thread, um die Funktion worker in einem neuen Thread zu starten. Der Hauptthread wartet nicht, bis die Funktion worker fertig ist, sondern setzt seine Ausführung fort. Wenn der worker - Thread fertig ist, haben wir jedoch keine einfache Möglichkeit, den Rückgabewert zu erhalten.

Schritt 5: Ausführen des threaded - Codes

Speichern Sie die Datei erneut und führen Sie sie mit demselben Befehl aus:

python ~/project/futures_demo.py

Sie werden bemerken, dass der Hauptthread weiterläuft, der worker - Thread läuft, aber wir nicht auf den Rückgabewert der Funktion worker zugreifen können.

Schritt 6: Manuelles Verwenden eines Future

Um das Problem des Abrufs des Rückgabewerts aus einem Thread zu lösen, können wir ein Future - Objekt verwenden. Fügen Sie den folgenden Code zur Datei futures_demo.py hinzu:

## Part 3: Using a Future to get the result
print("\n--- Part 3: Using a Future manually ---")

def do_work_with_future(x, y, future):
    """Wrapper that sets the result in the Future"""
    result = worker(x, y)
    future.set_result(result)

## Create a Future object
fut = Future()

## Start a thread that will set the result in the Future
t = threading.Thread(target=do_work_with_future, args=(2, 3, fut))
t.start()

print("Main thread continues...")
print("Waiting for the result...")
## Block until the result is available
result = fut.result()  ## This will wait until set_result is called
print(f"Got the result: {result}")

In diesem Code erstellen wir ein Future - Objekt und übergeben es an eine neue Funktion do_work_with_future. Diese Funktion ruft die Funktion worker auf und setzt dann das Ergebnis im Future - Objekt. Der Hauptthread kann dann die Methode result() des Future - Objekts verwenden, um das Ergebnis zu erhalten, wenn es verfügbar ist.

Schritt 7: Ausführen des Codes mit Future

Speichern Sie die Datei und führen Sie sie erneut aus:

python ~/project/futures_demo.py

Jetzt werden Sie sehen, dass wir erfolgreich den Rückgabewert aus der Funktion erhalten können, die im Thread läuft.

Schritt 8: Verwenden von ThreadPoolExecutor

Die Klasse ThreadPoolExecutor in Python erleichtert die Arbeit mit parallelen Aufgaben noch weiter. Fügen Sie den folgenden Code zur Datei futures_demo.py hinzu:

## Part 4: Using ThreadPoolExecutor (easier way)
print("\n--- Part 4: Using ThreadPoolExecutor ---")
with ThreadPoolExecutor() as executor:
    ## Submit the work to the executor
    future = executor.submit(worker, 2, 3)

    print("Main thread continues after submitting work...")
    print("Checking if the future is done:", future.done())

    ## Get the result (will wait if not ready)
    result = future.result()
    print("Now the future is done:", future.done())
    print(f"Final result: {result}")

Der ThreadPoolExecutor kümmert sich um die Erstellung und Verwaltung der Future - Objekte für Sie. Sie müssen nur die Funktion und ihre Argumente übermitteln, und er gibt ein Future - Objekt zurück, das Sie verwenden können, um das Ergebnis zu erhalten.

Schritt 9: Ausführen des vollständigen Codes

Speichern Sie die Datei ein letztes Mal und führen Sie sie aus:

python ~/project/futures_demo.py

Erklärung

  1. Normaler Funktionsaufruf: Wenn Sie eine Funktion auf normale Weise aufrufen, wartet das Programm, bis die Funktion fertig ist, und erhält direkt den Rückgabewert.
  2. Thread - Problem: Das Ausführen einer Funktion in einem separaten Thread hat einen Nachteil. Es gibt keine integrierte Möglichkeit, den Rückgabewert der Funktion zu erhalten, die in diesem Thread läuft.
  3. Manuelles Future: Indem wir ein Future - Objekt erstellen und es an den Thread übergeben, können wir das Ergebnis im Future setzen und dann das Ergebnis aus dem Hauptthread abrufen.
  4. ThreadPoolExecutor: Diese Klasse vereinfacht die parallele Programmierung. Sie kümmert sich um die Erstellung und Verwaltung der Future - Objekte für Sie, was es einfacher macht, Funktionen parallel auszuführen und ihre Rückgabewerte zu erhalten.

Future - Objekte haben mehrere nützliche Methoden:

  • result(): Diese Methode wird verwendet, um das Ergebnis der Funktion zu erhalten. Wenn das Ergebnis noch nicht bereit ist, wird sie warten, bis es es ist.
  • done(): Mit dieser Methode können Sie prüfen, ob die Berechnung der Funktion abgeschlossen ist.
  • add_done_callback(): Mit dieser Methode können Sie eine Funktion registrieren, die aufgerufen wird, wenn das Ergebnis bereit ist.

Dieses Muster ist in der parallelen Programmierung sehr wichtig, insbesondere wenn Sie Ergebnisse von Funktionen erhalten müssen, die parallel laufen.

Zusammenfassung

In diesem Lab haben Sie mehrere wichtige Muster für die Rückgabe von Werten aus Funktionen in Python gelernt. Erstens können Python - Funktionen mehrere Werte zurückgeben, indem sie diese in ein Tupel packen. Dies ermöglicht eine saubere und lesbare Rückgabe und Entpackung von Werten. Zweitens ist es bei Funktionen, die möglicherweise nicht immer gültige Ergebnisse liefern, üblich, None zurückzugeben, um die Abwesenheit eines Werts anzuzeigen. Als Alternative wurde auch das Auslösen von Ausnahmen (Exceptions) vorgestellt.

Schließlich fungiert in der parallelen Programmierung ein Future als Platzhalter für ein zukünftiges Ergebnis. Dies ermöglicht es Ihnen, Rückgabewerte von Funktionen zu erhalten, die in separaten Threads oder Prozessen laufen. Das Verständnis dieser Muster wird die Robustheit und Flexibilität Ihres Python - Codes verbessern. Für weitere Übungen können Sie verschiedene Fehlerbehandlungsstrategien ausprobieren, Futures mit anderen Typen der parallelen Ausführung verwenden und deren Anwendung in der asynchronen Programmierung mit async/await erkunden.