Grundlagen

Funktionen in Python: def, return, Argumente, *args und **kwargs, lambda und Typannotationen

Aktualisiert: · Skillaria.Top

Eine Funktion in Python ist ein benannter Codeblock, den Sie mit def definieren, mit Klammern aufrufen und der über return ein Ergebnis zurückgibt; ohne return liefert sie None. Argumente übergeben Sie der Reihe nach oder mit Namen, *args sammelt überzählige Werte in einem Tupel, **kwargs benannte Werte in einem Dictionary, und lambda erzeugt eine kurze anonyme Funktion aus einem einzigen Ausdruck.

Eine Funktion in Python ist ein benannter Codeblock, den Sie beliebig oft mit verschiedenen Eingabewerten aufrufen können. Sie wird mit def definiert, nimmt Werte über Parameter entgegen und liefert das Ergebnis über return zurück. Funktionen sind das wichtigste Mittel, um ein langes Skript in verständliche, überprüfbare Schritte zu zerlegen.

Eine Funktion definieren: def in Python

Die Definition beginnt mit def, danach folgen der Name, die Parameter in Klammern und ein Doppelpunkt. Der Körper wird mit vier Leerzeichen eingerückt. Die Einrückung ist in Python kein Schmuck, sondern Teil der Syntax.

def greet(name):
    """Gibt eine Begrüßung für den Benutzer zurück."""
    return f"Hallo, {name}!"

print(greet("Anna"))   # Hallo, Anna!

Ein Text in dreifachen Anführungszeichen direkt nach der Kopfzeile ist ein Docstring, die Dokumentation der Funktion. Ihn zeigt help(greet) und die Hinweise des Editors an. Funktionsnamen schreibt man nach der Konvention PEP 8 in snake_case: add_expense, get_rate.

Die Zeile def greet(name): nennt man Signatur der Funktion: Name plus Liste der Parameter (und Annotationen, falls vorhanden). Anhand der Signatur erkennt der Leser, wie die Funktion aufzurufen ist, ohne den Körper zu lesen.

Was return in Python macht

return beendet die Funktion und gibt einen Wert an den Aufrufer zurück. Code nach einem ausgeführten return läuft nicht mehr. Fehlt return oder steht es ohne Wert da, liefert die Funktion None.

def area(w, h):
    w * h          # Ergebnis wird berechnet und verworfen

print(area(3, 4))  # None

def area(w, h):
    return w * h

print(area(3, 4))  # 12

Ein vergessenes return ist einer der häufigsten Fehler von Einsteigern: Die Funktion „läuft“, liefert aber None, und der Fehler zeigt sich erst in der nächsten Zeile. Man kann auch mehrere Werte zurückgeben. Tatsächlich ist das ein Tupel, das sich bequem entpacken lässt:

def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([4, 1, 9])
print(low, high)   # 1 9

Ein frühes return vermeidet unnötige Verschachtelung: Zuerst behandeln Sie die Sonderfälle, danach folgt die eigentliche Logik.

def discount(price, is_member):
    if price <= 0:
        return 0
    if not is_member:
        return price
    return price * 0.9

Argumente einer Funktion in Python: positionale und benannte

Parameter sind die Namen in der Definition der Funktion, Argumente sind die Werte, die beim Aufruf übergeben werden. Sie können Argumente der Reihe nach (positional) oder mit Namen (benannt) übergeben. Benannte Argumente machen den Aufruf selbsterklärend, und die Reihenfolge spielt dann keine Rolle mehr.

def add_expense(title, amount, category="Sonstiges"):
    return {"title": title, "amount": amount, "category": category}

add_expense("Kaffee", 250)                          # positional
add_expense(amount=250, title="Kaffee")             # benannt
add_expense("Taxi", 600, category="Verkehr")        # gemischt

Ein Parameter mit Standardwert ist optional. Dabei gilt: Parameter mit Standardwert stehen nach den Pflichtparametern, und beim Aufruf kommen die positionalen Argumente vor den benannten. Sonst meldet Python einen SyntaxError.

Falle: veränderliche Standardwerte

Der Standardwert wird einmal berechnet, und zwar beim Ausführen der Zeile def, nicht bei jedem Aufruf. Ist er eine Liste oder ein Dictionary, wird dasselbe Objekt bei allen Aufrufen benutzt.

def add_item(item, acc=[]):
    acc.append(item)
    return acc

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2] — die Liste erinnert sich an den vorherigen Aufruf

Die richtige Lösung ist None als Standardwert und das Erzeugen eines neuen Objekts im Körper:

def add_item(item, acc=None):
    if acc is None:
        acc = []
    acc.append(item)
    return acc

*args und **kwargs: beliebig viele Argumente

Ein Stern vor einem Parameter sammelt alle überzähligen positionalen Argumente in einem Tupel (tuple). Zwei Sterne sammeln alle überzähligen benannten Argumente in einem Dictionary (dict). Die Namen args und kwargs sind nur eine Konvention, wichtig sind die Sterne.

def total(*args):
    return sum(args)

print(total(100, 250, 40))   # 390

def make_user(name, **kwargs):
    user = {"name": name}
    user.update(kwargs)
    return user

print(make_user("Anna", city="Hamburg", age=30))
# {'name': 'Anna', 'city': 'Hamburg', 'age': 30}

Dieselben Sterne wirken auch in die andere Richtung: Beim Aufruf entpacken sie eine Folge oder ein Dictionary in Argumente:

values = [3, 4]
print(pow(*values))          # 81

opts = {"sep": " | ", "end": "\n"}
print("a", "b", **opts)      # a | b

Nur positionale und nur benannte Parameter

Der Schrägstrich / und der Stern * in der Parameterliste wirken wie Schranken: Alles vor / kann nur der Reihe nach übergeben werden (die Syntax gibt es seit Python 3.8, PEP 570), alles nach * nur mit Namen.

def convert(amount, /, *, rate, currency="USD"):
    return round(amount / rate, 2), currency

convert(100, rate=90.5)          # (1.1, 'USD')
convert(amount=100, rate=90.5)   # TypeError: amount kann nur positional übergeben werden
convert(100, 90.5)               # TypeError: rate nur mit Namen

Nur benannte Parameter schützen vor Verwechslungen in Aufrufen wie convert(100, 90.5, "EUR"), bei denen unklar ist, was was ist.

Gültigkeitsbereich: die LEGB-Regel

Python sucht einen Namen in dieser Reihenfolge: Local (innerhalb der Funktion), Enclosing (in der äußeren Funktion), Global (auf Modulebene), Built-in (eingebaute Namen wie len). Eine globale Variable innerhalb einer Funktion zu lesen ist erlaubt. Um ihr einen neuen Wert zuzuweisen, brauchen Sie die Anweisung global.

limit = 1000

def check(amount):
    return amount > limit    # Lesen der globalen Variable — erlaubt

def raise_limit():
    global limit
    limit = 2000             # ohne global würde eine lokale Variable entstehen

In der Praxis sollten Sie global meiden: Eine Funktion, die einen äußeren Zustand verändert, lässt sich schlecht testen. Besser ist es, den Wert als Argument zu übergeben und das Ergebnis zurückzugeben.

Verschachtelte Funktionen und Closures

Eine Funktion kann innerhalb einer anderen definiert werden. Die innere Funktion sieht die Variablen der äußeren auch dann, wenn die äußere bereits beendet ist. Das nennt man Closure. Um eine solche Variable zu verändern, dient nonlocal.

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

c = make_counter()
print(c(), c(), c())   # 1 2 3

Closures bilden die Grundlage für Dekoratoren und Funktionsfabriken.

Eine Funktion ist auch ein Objekt

In Python ist eine Funktion ein gewöhnliches Objekt: Sie können sie in einer Variablen speichern, an eine andere Funktion übergeben, aus einer Funktion zurückgeben oder in einem Dictionary ablegen. Sie besitzt Attribute wie __name__, __doc__, __defaults__ und __annotations__.

def double(x):
    return x * 2

f = double
print(f(5), f.__name__)                # 10 double
print(list(map(double, [1, 2, 3])))   # [2, 4, 6]

Lambda in Python: anonyme Funktionen

Eine lambda-Funktion ist eine kurze Funktion ohne Namen, deren Körper aus einem einzigen Ausdruck besteht. Das Ergebnis des Ausdrucks wird automatisch zurückgegeben, ein return schreibt man nicht.

square = lambda x: x ** 2
print(square(4))   # 16

expenses = [("Kaffee", 250), ("Taxi", 600), ("Mittagessen", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses)  # [('Taxi', 600), ('Mittagessen', 450), ('Kaffee', 250)]

print(list(filter(lambda n: n % 2 == 0, range(10))))  # [0, 2, 4, 6, 8]

Lambda-Funktionen in Python eignen sich gut als kurzes Argument für sorted, max, map oder filter. Eine Lambda einer Variablen zuzuweisen, wie square oben, empfiehlt PEP 8 nicht. Wenn eine Funktion einen Namen braucht, schreiben Sie def. Dann steht ein sinnvoller __name__ im Traceback, und es gibt Platz für einen Docstring.

deflambda
Namevorhandenkeiner (<lambda>)
Körperbeliebig viele Anweisungenein einziger Ausdruck
returnausdrücklichimplizit
Docstring, Annotationenjanein
Wo sinnvollüberallkurzer key oder Callback

Typannotationen: Hinweise, keine Prüfungen

Annotationen beschreiben die erwarteten Typen von Parametern und Ergebnis. Python selbst prüft sie zur Laufzeit nicht: Eine Funktion mit x: int nimmt problemlos eine Zeichenkette an. Geprüft werden Annotationen von externen Werkzeugen wie mypy, pyright oder ty und vom Editor, der Fehler schon vor dem Start markiert.

def add_expense(title: str, amount: float, category: str = "Sonstiges") -> dict[str, object]:
    return {"title": title, "amount": amount, "category": category}

def area(w: float, h: float) -> float:
    w * h   # mypy: Missing return statement

Eine Prüfung ohne Installation starten Sie mit uvx mypy .. Das Beispiel oben zeigt den Nutzen der Annotationen: Ein vergessenes return bei -> float findet der Analysator sofort.

Seit Python 3.12 (PEP 695) schreibt man generische Funktionen ohne TypeVar: Der Typparameter steht in eckigen Klammern, ein Typalias wird mit dem Wort type deklariert.

def first[T](xs: list[T]) -> T:
    return xs[0]

type Money = int | float

In Python 3.14 (PEP 649) sind Annotationen lazy geworden: Sie werden erst beim ersten Zugriff ausgewertet. Deshalb können Sie auf eine Klasse verweisen, die weiter unten definiert ist, ohne Anführungszeichen, und from __future__ import annotations ist nicht mehr nötig.

„Nicht übergeben“ oder „None übergeben“: Sentinel in 3.15

Der Standardwert None erlaubt keine Unterscheidung zwischen „Argument nicht übergeben“ und „bewusst None übergeben“. Jahrelang löste man das mit einem selbst gebauten Platzhalter:

_MISSING = object()

def get_rate(currency, default=_MISSING):
    rates = {"USD": 90.5}
    if currency in rates:
        return rates[currency]
    if default is _MISSING:
        raise KeyError(currency)
    return default

In Python 3.15 gibt es die eingebaute Funktion sentinel (PEP 661): MISSING = sentinel("MISSING") erzeugt einen eindeutigen Marker mit verständlicher Darstellung, der beim Kopieren er selbst bleibt, sich über pickle serialisieren lässt und sich in einer Annotation wie float | MISSING verwenden lässt. Auf älteren Versionen funktioniert weiterhin der Trick mit object().

Typische Fehler mit Funktionen

  • return vergessen — die Funktion liefert None.
  • Klammern vergessen — print(greet) gibt das Funktionsobjekt aus, nicht das Ergebnis des Aufrufs.
  • Veränderlicher Standardwert — eine gemeinsame Liste für alle Aufrufe.
  • Falsche Anzahl von Argumenten — TypeError: greet() missing 1 required positional argument: 'name'.
  • Der Name entspricht einer eingebauten Funktion — eine Funktion namens list oder sum verdeckt die eingebaute.

Fazit

Funktionen in Python werden mit def definiert, liefern ihr Ergebnis über return und nehmen Argumente der Reihe nach oder mit Namen entgegen. *args und **kwargs bringen Flexibilität, / und * sorgen für strikte Aufrufe, lambda ist kurz und praktisch für einmalige Funktionen, und Typannotationen finden zusammen mit mypy Fehler vor dem Start. Im Kurs wird dieses Thema praktisch behandelt: Der Ausgabentracker wird in Funktionen mit Annotationen zerlegt und mit mypy geprüft.

Häufige Fragen

Worin unterscheidet sich ein Parameter von einem Argument?

Ein Parameter ist der Name in der Definition der Funktion, ein Argument ist der konkrete Wert beim Aufruf. In def greet(name) ist name der Parameter, in greet("Anna") ist die Zeichenkette "Anna" das Argument.

Kann ich eine Funktion vor ihrer Definition aufrufen?

Auf Modulebene nicht: Die Zeile def muss vor dem Aufruf ausgeführt werden, sonst gibt es einen NameError. Innerhalb einer anderen Funktion können Sie aber eine später definierte Funktion aufrufen, sofern der Aufruf nach ihrer Definition erfolgt.

Wie viele Werte kann eine Funktion zurückgeben?

Formal nur einen, aber dieser Wert kann ein Tupel sein. return a, b gibt ein Tupel aus zwei Elementen zurück, das Sie mit x, y = f() entpacken.

Muss ich Typannotationen schreiben?

Nein, Python funktioniert auch ohne sie. Im Code, den andere Menschen lesen, erleichtern sie die Wartung erheblich, und mypy oder pyright finden damit Fehler vor dem Start.

Kann ich in einer lambda ein if verwenden?

Nur als bedingten Ausdruck: lambda x: "gerade" if x % 2 == 0 else "ungerade". Anweisungen, Schleifen und Zuweisungen sind in einer lambda nicht erlaubt.

Das Thema im Video lernen

Dieses Thema wird im Kurs „Python 3.15: от новичка до профи“ ausführlich mit Praxis behandelt.

Zum Kurs