Parameter und Argumente in Python
Lerne alles ueber Parameter und Argumente in Python: positionale Parameter, Keyword-Argumente, *args, **kwargs und Type Hints.
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:
- Positionale Parameter (Pflichtparameter)
*args(beliebig viele positionale)- Keyword-Parameter (mit oder ohne Standardwert)
**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
*argswenn die Anzahl der gleichartigen Eingaben variiert (z.B.summe(1, 2, 3))**kwargswenn 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!