Zum Inhalt springen
Python Anfänger 2 min

Parameter und Argumente in Python

Lerne alles ueber Parameter und Argumente in Python: positionale Parameter, Keyword-Argumente, *args, **kwargs und Type Hints.

Aktualisiert:

Parameter und Argumente in Python

Funktionen werden erst richtig maechtig, wenn du verstehst, wie du ihnen Daten uebergeben kannst. Python bietet ein unglaublich flexibles System fuer Parameter und Argumente. In diesem Tutorial lernst du alle Varianten kennen.

Der Unterschied: Parameter vs. Argumente

Kurz zur Begriffsklaerung, die oft verwechselt werden:

  • Parameter sind die Variablen in der Funktionsdefinition
  • Argumente sind die konkreten Werte beim Funktionsaufruf
def begruessung(name):     # "name" ist ein PARAMETER
    print(f"Hallo, {name}!")

begruessung("Anna")        # "Anna" ist ein ARGUMENT

In der Praxis werden die Begriffe oft synonym verwendet - das ist voellig in Ordnung.

Positionale Parameter

Die einfachste Form: Argumente werden der Reihe nach zugeordnet.

def stelle_vor(vorname, nachname, alter):
    print(f"{vorname} {nachname}, {alter} Jahre alt")

stelle_vor("Anna", "Mueller", 28)
# Ausgabe: Anna Mueller, 28 Jahre alt

Die Reihenfolge ist entscheidend:

stelle_vor("Mueller", "Anna", 28)
# Ausgabe: Mueller Anna, 28 Jahre alt  (Falsch!)

Wenn du zu wenige oder zu viele Argumente uebergibst, gibt es einen Fehler:

# stelle_vor("Anna")               # TypeError: missing 2 required positional arguments
# stelle_vor("Anna", "M", 28, "X") # TypeError: takes 3 positional arguments but 4 were given

Keyword-Argumente

Mit Keyword-Argumenten (benannte Argumente) kannst du die Reihenfolge ignorieren, indem du den Parameternamen angibst:

def stelle_vor(vorname, nachname, alter):
    print(f"{vorname} {nachname}, {alter} Jahre alt")

# Mit Keyword-Argumenten ist die Reihenfolge egal
stelle_vor(alter=28, nachname="Mueller", vorname="Anna")
# Ausgabe: Anna Mueller, 28 Jahre alt

# Mischung: Erst positionale, dann keyword
stelle_vor("Anna", alter=28, nachname="Mueller")
# Ausgabe: Anna Mueller, 28 Jahre alt

Wichtige Regel: Positionale Argumente muessen vor Keyword-Argumenten stehen.

# Das geht NICHT:
# stelle_vor(vorname="Anna", "Mueller", 28)
# SyntaxError: positional argument follows keyword argument

Keyword-Argumente machen den Code lesbarer, besonders bei vielen Parametern:

# Schwer zu lesen - was bedeutet True, False, 3?
erstelle_bericht("verkauf", True, False, 3)

# Viel klarer mit Keywords!
erstelle_bericht("verkauf", sortiert=True, absteigend=False, max_eintraege=3)

Standardwerte (Default-Parameter)

Du kannst Parametern Standardwerte zuweisen. Wenn beim Aufruf kein Argument uebergeben wird, wird der Standardwert verwendet:

def begruessung(name, gruss="Hallo"):
    print(f"{gruss}, {name}!")

begruessung("Anna")              # Ausgabe: Hallo, Anna!
begruessung("Anna", "Guten Tag") # Ausgabe: Guten Tag, Anna!
begruessung("Anna", gruss="Hi")  # Ausgabe: Hi, Anna!

Mehrere Standardwerte:

def erstelle_profil(name, alter=0, stadt="Unbekannt", beruf="Nicht angegeben"):
    print(f"Name:  {name}")
    print(f"Alter: {alter}")
    print(f"Stadt: {stadt}")
    print(f"Beruf: {beruf}")
    print("-" * 30)

# Nur Pflichtparameter
erstelle_profil("Anna")

# Einige optionale Parameter
erstelle_profil("Max", alter=30, beruf="Entwickler")

# Alle Parameter
erstelle_profil("Lisa", 25, "Berlin", "Designerin")

Wichtige Regel: Parameter mit Standardwerten muessen nach Parametern ohne Standardwerte stehen:

# RICHTIG
def funktion(pflicht, optional="Standard"):
    pass

# FALSCH - SyntaxError!
# def funktion(optional="Standard", pflicht):
#     pass

*args - Beliebig viele positionale Argumente

Mit *args kann eine Funktion eine beliebige Anzahl positionaler Argumente annehmen. Innerhalb der Funktion ist args ein Tuple:

def summe(*args):
    """Addiert beliebig viele Zahlen."""
    print(f"Argumente: {args}")
    print(f"Typ: {type(args)}")
    return sum(args)

print(summe(1, 2, 3))          # Argumente: (1, 2, 3) -> 6
print(summe(10, 20, 30, 40))   # Argumente: (10, 20, 30, 40) -> 100
print(summe(5))                # Argumente: (5,) -> 5
print(summe())                 # Argumente: () -> 0

*args kann mit normalen Parametern kombiniert werden:

def einkauf(kunde, *artikel):
    """Erstellt einen Kassenbon."""
    print(f"Kunde: {kunde}")
    print("Artikel:")
    for a in artikel:
        print(f"  - {a}")
    print(f"Gesamt: {len(artikel)} Artikel")

einkauf("Anna", "Brot", "Milch", "Butter")
# Ausgabe:
# Kunde: Anna
# Artikel:
#   - Brot
#   - Milch
#   - Butter
# Gesamt: 3 Artikel

Du kannst auch eine bestehende Liste mit * entpacken:

zahlen = [1, 2, 3, 4, 5]
print(summe(*zahlen))  # Entpackt die Liste -> summe(1, 2, 3, 4, 5) -> 15

**kwargs - Beliebig viele Keyword-Argumente

Mit **kwargs kann eine Funktion beliebig viele Keyword-Argumente annehmen. Innerhalb der Funktion ist kwargs ein Dictionary:

def zeige_infos(**kwargs):
    """Gibt alle uebergebenen Informationen aus."""
    print(f"Typ: {type(kwargs)}")
    for schluessel, wert in kwargs.items():
        print(f"  {schluessel}: {wert}")

zeige_infos(name="Anna", alter=28, stadt="Berlin")
# Ausgabe:
#   name: Anna
#   alter: 28
#   stadt: Berlin

Praktisches Beispiel - eine flexible Konfigurationsfunktion:

def erstelle_html_tag(tag, inhalt, **attribute):
    """Erstellt einen HTML-Tag mit beliebigen Attributen."""
    attr_string = ""
    for schluessel, wert in attribute.items():
        # class_ wird zu class (da class ein Python-Keyword ist)
        attr_name = schluessel.rstrip("_")
        attr_string += f' {attr_name}="{wert}"'

    return f"<{tag}{attr_string}>{inhalt}</{tag}>"

# Flexible Verwendung
print(erstelle_html_tag("p", "Hallo Welt"))
# <p>Hallo Welt</p>

print(erstelle_html_tag("div", "Inhalt", class_="container", id="main"))
# <div class="container" id="main">Inhalt</div>

print(erstelle_html_tag("a", "Klick mich", href="https://python.org", target="_blank"))
# <a href="https://python.org" target="_blank">Klick mich</a>

Du kannst ein Dictionary mit ** entpacken:

einstellungen = {"farbe": "blau", "groesse": 14, "fett": True}
zeige_infos(**einstellungen)  # Entpackt das Dict -> zeige_infos(farbe="blau", ...)

Die Reihenfolge der Parameter

Wenn du alle Parameterarten mischst, muss diese Reihenfolge eingehalten werden:

  1. Positionale Parameter (Pflichtparameter)
  2. *args (beliebig viele positionale)
  3. Keyword-Parameter (mit oder ohne Standardwert)
  4. **kwargs (beliebig viele Keyword-Argumente)
def super_funktion(pflicht, *args, option="Standard", **kwargs):
    print(f"Pflicht: {pflicht}")
    print(f"*args: {args}")
    print(f"Option: {option}")
    print(f"**kwargs: {kwargs}")

super_funktion("Hallo", 1, 2, 3, option="Spezial", extra="Wert", debug=True)
# Ausgabe:
# Pflicht: Hallo
# *args: (1, 2, 3)
# Option: Spezial
# **kwargs: {'extra': 'Wert', 'debug': True}

Ein praxisnahes Beispiel:

def erstelle_bericht(titel, *daten, format="text", **optionen):
    """Erstellt einen flexiblen Bericht."""
    print(f"=== {titel.upper()} ===")
    print(f"Format: {format}")

    if daten:
        print("Daten:")
        for d in daten:
            print(f"  - {d}")

    if optionen:
        print("Optionen:")
        for k, v in optionen.items():
            print(f"  {k}: {v}")

erstelle_bericht(
    "Monatsbericht",
    "Umsatz: 50000", "Kosten: 30000", "Gewinn: 20000",
    format="pdf",
    autor="Anna",
    abteilung="Vertrieb"
)

Type Hints (Einfuehrung)

Seit Python 3.5 kannst du Type Hints verwenden, um anzugeben, welche Typen eine Funktion erwartet und zurueckgibt. Type Hints sind optional und werden von Python nicht erzwungen, aber sie verbessern die Lesbarkeit und ermoeglichen bessere Tool-Unterstuetzung:

def begruessung(name: str) -> str:
    """Erstellt eine Begruessung."""
    return f"Hallo, {name}!"

def addiere(a: int, b: int) -> int:
    """Addiert zwei Zahlen."""
    return a + b

def berechne_durchschnitt(zahlen: list[float]) -> float:
    """Berechnet den Durchschnitt einer Zahlenliste."""
    return sum(zahlen) / len(zahlen)

def finde_person(name: str, alter: int = 0) -> dict | None:
    """Sucht eine Person in der Datenbank."""
    # ... Suche ...
    return None

Type Hints mit *args und **kwargs:

def summe(*args: int) -> int:
    return sum(args)

def konfiguriere(**kwargs: str) -> dict[str, str]:
    return dict(kwargs)

Mehrere Rueckgabetypen:

def teile(a: float, b: float) -> tuple[float, str]:
    """Gibt Ergebnis und Status zurueck."""
    if b == 0:
        return 0.0, "Fehler: Division durch Null"
    return a / b, "OK"

Der Fallstrick: Mutable Default-Argumente

Dies ist einer der bekanntesten Python-Fallstricke. Verwende niemals ein veraenderbares Objekt (Liste, Dictionary, Set) als Standardwert:

# GEFAEHRLICH! Die Liste wird zwischen Aufrufen geteilt!
def fuege_hinzu_falsch(element, liste=[]):
    liste.append(element)
    return liste

print(fuege_hinzu_falsch("a"))   # ['a']          - sieht gut aus
print(fuege_hinzu_falsch("b"))   # ['a', 'b']     - Ueberraschung!
print(fuege_hinzu_falsch("c"))   # ['a', 'b', 'c'] - Die Liste waechst weiter!

Warum passiert das? Der Standardwert [] wird nur einmal erstellt, wenn die Funktion definiert wird - nicht bei jedem Aufruf. Alle Aufrufe teilen sich dieselbe Liste.

Die richtige Loesung: Verwende None als Standardwert und erstelle das Objekt innerhalb der Funktion:

# RICHTIG: None als Standardwert verwenden
def fuege_hinzu_richtig(element, liste=None):
    if liste is None:
        liste = []
    liste.append(element)
    return liste

print(fuege_hinzu_richtig("a"))   # ['a']
print(fuege_hinzu_richtig("b"))   # ['b']  - Jedes Mal eine neue Liste!
print(fuege_hinzu_richtig("c"))   # ['c']

Das gleiche gilt fuer Dictionaries:

# FALSCH
def erstelle_eintrag_falsch(name, daten={}):
    daten["name"] = name
    return daten

# RICHTIG
def erstelle_eintrag_richtig(name, daten=None):
    if daten is None:
        daten = {}
    daten["name"] = name
    return daten

Praxis-Beispiele

Beispiel 1: Flexible Logging-Funktion

from datetime import datetime

def log(nachricht: str, *tags: str, level: str = "INFO", **details) -> str:
    """Erstellt einen formatierten Log-Eintrag."""
    zeitstempel = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    eintrag = f"[{zeitstempel}] [{level}] {nachricht}"

    if tags:
        tag_string = ", ".join(tags)
        eintrag += f" (Tags: {tag_string})"

    if details:
        detail_string = ", ".join(f"{k}={v}" for k, v in details.items())
        eintrag += f" | {detail_string}"

    return eintrag

# Einfacher Log
print(log("Server gestartet"))

# Mit Tags und Level
print(log("Benutzer angemeldet", "auth", "user", level="DEBUG"))

# Mit allem
print(log("Fehler aufgetreten", "error", "critical",
          level="ERROR", datei="main.py", zeile=42))

Beispiel 2: Konfigurierbare Formatierungsfunktion

def formatiere_tabelle(
    daten: list[dict],
    *spalten: str,
    trennzeichen: str = " | ",
    kopfzeile: bool = True,
    ausrichtung: str = "links"
) -> str:
    """Erstellt eine formatierte Texttabelle."""
    if not daten:
        return "Keine Daten vorhanden."

    # Spalten bestimmen
    if not spalten:
        spalten = tuple(daten[0].keys())

    # Spaltenbreiten berechnen
    breiten = {}
    for spalte in spalten:
        werte = [str(zeile.get(spalte, "")) for zeile in daten]
        breiten[spalte] = max(len(spalte), max(len(w) for w in werte))

    # Zeilen formatieren
    zeilen = []

    if kopfzeile:
        kopf = trennzeichen.join(
            spalte.ljust(breiten[spalte]) for spalte in spalten
        )
        zeilen.append(kopf)
        zeilen.append("-" * len(kopf))

    for eintrag in daten:
        zeile = trennzeichen.join(
            str(eintrag.get(spalte, "")).ljust(breiten[spalte])
            for spalte in spalten
        )
        zeilen.append(zeile)

    return "\n".join(zeilen)

# Beispieldaten
mitarbeiter = [
    {"name": "Anna", "abteilung": "IT", "gehalt": 55000},
    {"name": "Max", "abteilung": "Marketing", "gehalt": 48000},
    {"name": "Lisa", "abteilung": "IT", "gehalt": 62000},
]

print(formatiere_tabelle(mitarbeiter))
print()
print(formatiere_tabelle(mitarbeiter, "name", "gehalt", trennzeichen=" : "))

Beispiel 3: Funktions-Wrapper mit *args und **kwargs

def wiederhole(anzahl: int = 3):
    """Gibt eine Funktion zurueck, die eine andere Funktion n-mal ausfuehrt."""
    def wrapper(funktion, *args, **kwargs):
        ergebnisse = []
        for i in range(anzahl):
            ergebnis = funktion(*args, **kwargs)
            ergebnisse.append(ergebnis)
        return ergebnisse
    return wrapper

def wuerfle():
    import random
    return random.randint(1, 6)

wiederhole_5 = wiederhole(5)
wuerfe = wiederhole_5(wuerfle)
print(f"5 Wuerfe: {wuerfe}")

Uebungen

Uebung 1: Flexible Begruessung

Schreibe eine Funktion begruessung(), die verschiedene Aufrufarten unterstuetzt:

def begruessung(name: str, *titel: str, sprache: str = "de", **extras) -> str:
    """Erstellt eine flexible Begruessung."""
    gruss = {"de": "Hallo", "en": "Hello", "fr": "Bonjour"}
    anrede = gruss.get(sprache, "Hallo")

    titel_str = " ".join(titel)
    if titel_str:
        vollstaendig = f"{titel_str} {name}"
    else:
        vollstaendig = name

    nachricht = f"{anrede}, {vollstaendig}!"

    if extras:
        zusatz = ", ".join(f"{k}: {v}" for k, v in extras.items())
        nachricht += f" ({zusatz})"

    return nachricht

# Tests
print(begruessung("Anna"))
# Hallo, Anna!

print(begruessung("Mueller", "Dr.", "Prof.", sprache="de"))
# Hallo, Dr. Prof. Mueller!

print(begruessung("Smith", sprache="en", firma="Google", rolle="CEO"))
# Hello, Smith! (firma: Google, rolle: CEO)

Uebung 2: Dictionary-Merge-Funktion

def merge_dicts(*dicts: dict, strategie: str = "ueberschreiben") -> dict:
    """Fuegt mehrere Dictionaries zusammen.

    Strategien:
    - "ueberschreiben": Spaetere Werte ueberschreiben fruehere
    - "behalten": Erste Werte werden behalten
    - "liste": Konflikte werden als Liste gespeichert
    """
    ergebnis = {}

    for d in dicts:
        for schluessel, wert in d.items():
            if schluessel not in ergebnis:
                ergebnis[schluessel] = wert
            elif strategie == "ueberschreiben":
                ergebnis[schluessel] = wert
            elif strategie == "behalten":
                pass  # Ersten Wert behalten
            elif strategie == "liste":
                if isinstance(ergebnis[schluessel], list):
                    ergebnis[schluessel].append(wert)
                else:
                    ergebnis[schluessel] = [ergebnis[schluessel], wert]

    return ergebnis

# Tests
a = {"x": 1, "y": 2}
b = {"y": 3, "z": 4}
c = {"z": 5, "w": 6}

print(merge_dicts(a, b, c))
# {'x': 1, 'y': 3, 'z': 5, 'w': 6}

print(merge_dicts(a, b, c, strategie="behalten"))
# {'x': 1, 'y': 2, 'z': 4, 'w': 6}

print(merge_dicts(a, b, c, strategie="liste"))
# {'x': 1, 'y': [2, 3], 'z': [4, 5], 'w': 6}

Uebung 3: Validierungsfunktion mit Type Hints

def validiere(
    wert,
    typ: type = str,
    min_wert: float | None = None,
    max_wert: float | None = None,
    min_laenge: int | None = None,
    max_laenge: int | None = None,
    erlaubte_werte: list | None = None,
    muster: str | None = None
) -> tuple[bool, str]:
    """Validiert einen Wert anhand verschiedener Kriterien."""
    import re

    if not isinstance(wert, typ):
        return False, f"Erwartet {typ.__name__}, erhalten {type(wert).__name__}"

    if isinstance(wert, (int, float)):
        if min_wert is not None and wert < min_wert:
            return False, f"Wert {wert} ist kleiner als Minimum {min_wert}"
        if max_wert is not None and wert > max_wert:
            return False, f"Wert {wert} ist groesser als Maximum {max_wert}"

    if isinstance(wert, str):
        if min_laenge is not None and len(wert) < min_laenge:
            return False, f"Laenge {len(wert)} ist kuerzer als {min_laenge}"
        if max_laenge is not None and len(wert) > max_laenge:
            return False, f"Laenge {len(wert)} ist laenger als {max_laenge}"
        if muster is not None and not re.match(muster, wert):
            return False, f"Entspricht nicht dem Muster: {muster}"

    if erlaubte_werte is not None and wert not in erlaubte_werte:
        return False, f"Wert nicht erlaubt. Erlaubt: {erlaubte_werte}"

    return True, "Gueltig"

# Tests
print(validiere("Hallo", str, min_laenge=3))        # (True, 'Gueltig')
print(validiere(42, int, min_wert=0, max_wert=100))  # (True, 'Gueltig')
print(validiere(-5, int, min_wert=0))                # (False, 'Wert -5 ist kleiner als Minimum 0')
print(validiere("ja", str, erlaubte_werte=["ja", "nein"]))  # (True, 'Gueltig')

Pro-Tipp: Wann welche Parameter-Art verwenden?

Eine Faustregel fuer die Wahl der Parameter-Art:

  • Positionale Parameter fuer die 1-3 wichtigsten, immer benoetigten Werte
  • Keyword mit Standardwert fuer optionale Konfiguration
  • *args wenn die Anzahl der gleichartigen Eingaben variiert (z.B. summe(1, 2, 3))
  • **kwargs wenn die Funktion komplett flexibel sein soll (z.B. bei Wrappern oder Konfigurationen)

Und denke immer an den Mutable-Default-Fallstrick: Verwende None statt [] oder {} als Standardwert. Dies ist einer der haeufigsten Fehler in Python und kann zu sehr schwer auffindbaren Bugs fuehren!

Zurück zum Python Kurs