mitmario.dev

Mehrere Rückgaben

Python Sandbox 4 Min Lesezeit 6 BeispieleLektion 4 von 9

return gibt einen Wert zurück, und mit return ist die Funktion zu Ende. Manche Fragen haben aber zwei Antworten. Wie viele Minuten und wie viele Sekunden sind 135 Sekunden? Wie viel kam diesen Monat herein, und wie viel ging hinaus? Zwei Funktionen, die beide dieselbe Liste durchgehen, wären doppelte Arbeit.

Ein Tupel zurückgeben

Zwei Werte auf einmal
def minuten_und_sekunden(gesamt):
    return gesamt // 60, gesamt % 60


ergebnis = minuten_und_sekunden(135)
print(ergebnis)
print(type(ergebnis))
minuten, sekunden = minuten_und_sekunden(135)
print(f"{minuten} Minuten und {sekunden} Sekunden")

Hinter return dürfen mehrere Werte stehen, mit Kommas getrennt. Zurück kommt dann ein Tupel, und in ergebnis siehst du es als (2, 15), Klammern inklusive.

In Lektion 5.7 stand, dass ein Tupel durch das Komma entsteht und nicht durch die Klammer. Hier zahlt sich das aus. return gesamt // 60, gesamt % 60 ist ein einziger Wert, ein Tupel aus zwei Zahlen. Die Funktion gibt also auch hier genau eine Sache zurück. Die besteht nur aus zwei Teilen.

Mit einem Tupel in einer Variablen bist du aber noch nicht weit, denn bei ergebnis[0] und ergebnis[1] weiß niemand, was drinsteht. Deshalb packt man es beim Aufruf meistens gleich aus. Links stehen so viele Namen, wie Werte zurückkommen, und jeder Name bekommt seinen Wert in der Reihenfolge, in der sie hinter dem return stehen. Die Rechnung selbst kennst du schon, ganzzahlig geteilt und der Rest.

Python hat für genau diese Rechnung übrigens schon eine eingebaute Funktion, divmod(135, 60), und die gibt ebenfalls ein Tupel zurück, (2, 15).

Einnahmen und Ausgaben
def bilanz(buchungen):
    rein = 0
    raus = 0
    for betrag in buchungen:
        if betrag > 0:
            rein += betrag
        else:
            raus += betrag
    return rein, raus


kontoauszug = [2450.00, -890.00, -64.90, -23.40, 120.00, -310.75]
einnahmen, ausgaben = bilanz(kontoauszug)
print(f"Rein: {einnahmen:.2f} Euro")
print(f"Raus: {ausgaben:.2f} Euro")
print(f"Übrig: {einnahmen + ausgaben:.2f} Euro")

Ein Kontoauszug wird einmal durchgegangen, und am Ende stehen zwei Summen fest. Was hereinkam und was hinausging. In der Schleife landet jeder Betrag nach seinem Vorzeichen auf einem der beiden Stapel, und return rein, raus gibt beide zurück.

Beim Aufruf heißen sie anders als in der Funktion, nämlich einnahmen und ausgaben. Das ist in Ordnung, denn ausgepackt wird nach der Reihenfolge und nicht nach dem Namen. Wer die Namen links vertauscht, hat deshalb die Ausgaben unter einnahmen stehen, und Python merkt nichts davon. Das ist derselbe leise Fehler wie bei den vertauschten Argumenten in der letzten Lektion.

Ein Name zu viel

Drei Namen für zwei Werte
def minuten_und_sekunden(gesamt):
    return gesamt // 60, gesamt % 60


stunden, minuten, sekunden = minuten_und_sekunden(4000)

Die Funktion gibt zwei Werte zurück, links stehen aber drei Namen. Das endet mit ValueError: not enough values to unpack (expected 3, got 2). Python sollte drei Werte auspacken und hat nur zwei bekommen. Mit zu wenigen Namen heißt es andersherum too many values to unpack. Beide Meldungen nennen die Zahlen, und damit findest du fast immer die Zeile, in der ein Wert vergessen oder einer zu viel erwartet wurde.

Wenn es nichts zu rechnen gibt

Die Spanne einer leeren Liste
def spanne(preise):
    return min(preise), max(preise)


print(spanne([2.49, 0.99, 7.99]))
print(spanne([]))

Der billigste und der teuerste Preis einer Liste, das sind min und max. Bei drei Preisen klappt das, bei keinem nicht. Dann kommt ValueError: min() iterable argument is empty. Von nichts gibt es kein Kleinstes.

Eine leere Liste ist aber nichts Ungewöhnliches. Ein Monat ohne Buchungen, eine Suche ohne Treffer, ein Zettel, auf dem noch nichts steht. Die Funktion sollte das aushalten, und zwar so, dass man es an der Stelle des Aufrufs merkt.

Für jeden Namen ein None
def spanne(preise):
    if not preise:
        return None, None
    return min(preise), max(preise)


billigste, teuerste = spanne([2.49, 0.99, 7.99])
print(billigste, teuerste)
billigste, teuerste = spanne([])
if billigste is None:
    print("Keine Preise, keine Spanne.")

Wenn die Liste leer ist, gibt die Funktion jetzt gleich am Anfang zurück, mit dem frühen return aus Lektion 8.2. Sie gibt zweimal None zurück, das „kein Wert“. Beim Aufruf werden wie immer zwei Namen ausgepackt, und danach wird mit is None gefragt, ob es überhaupt eine Spanne gibt.

Zweimal None und nicht einmal, denn wer zwei Werte verspricht, liefert auch im leeren Fall zwei. An dieser Stelle vertut man sich gern. Mit nur return None käme beim Auspacken ein einzelnes None an, wo zwei Werte erwartet werden, und das Programm endet mit TypeError: cannot unpack non-iterable NoneType object.

Ein Tupel oder ein Dictionary

Ein Dictionary statt eines Tupels
def kassensturz(buchungen):
    rein = sum([b for b in buchungen if b > 0])
    raus = sum([b for b in buchungen if b < 0])
    return {
        "rein": rein,
        "raus": raus,
        "uebrig": rein + raus,
        "buchungen": len(buchungen),
    }


ergebnis = kassensturz([2450.00, -890.00, -64.90, -23.40, 120.00, -310.75])
print(f"{ergebnis['buchungen']} Buchungen, übrig: {ergebnis['uebrig']:.2f} Euro")

Ein Tupel ist gut, solange es wenige Werte sind und sich ihre Reihenfolge von selbst versteht. Minuten vor Sekunden, das Kleinste vor dem Größten. Bei vier Werten weiß beim Aufruf niemand mehr, ob die Zahl der Buchungen an dritter oder an vierter Stelle kam. Dann ist ein Dictionary die bessere Form. Du holst dir darin, was du brauchst, über den Namen, und die Reihenfolge ist egal.

Die beiden Summen werden diesmal mit einer List Comprehension ausgerechnet. Und {...} über mehrere Zeilen geschrieben liest sich wie eine kleine Tabelle.

Eine feste Grenze gibt es nicht. Ein Dictionary lohnt sich, sobald man beim Auspacken überlegen muss, welcher Wert wohin gehört. Das Kleinste, das Größte und der Durchschnitt gehen als Tupel noch gut, weil man sie in dieser Reihenfolge auch sagt.

Was die Aufgabe will

Eine Wetterstation misst jeden Tag die Temperatur. Aus einer Liste solcher Messwerte soll eine Funktion den kleinsten, den größten und den Durchschnitt auf einmal zurückgeben. Und auch ein Tag ohne Messung darf sie nicht umwerfen.

Zum Mitnehmen

Stehen hinter return mehrere Werte mit Kommas dazwischen, kommt ein Tupel zurück, und beim Aufruf packst du es gleich in ebenso viele Namen aus. Auch bei einer leeren Liste kommen so viele Werte zurück, notfalls lauter None. Bei vielen Werten ist ein Dictionary oft die bessere Form.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 6 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.