Python *args und **kwargs erklärt: Flexible Funktionen mit klaren Beispielen

  1. Startseite
  2. Blog
  3. Softwareentwicklung

Softwareentwicklung

Python *args und **kwargs erklärt: Flexible Funktionen mit klaren Beispielen

Diese Sterne in Funktionssignaturen sind weniger mysteriös, als sie aussehen. Erfahren Sie, wie *args und **kwargs Argumente sammeln, wie dieselben Stars sie entpacken und wie Sie Funktionssignaturen entwerfen, die schwer zu missbrauchen sind.

8 Min. Lesezeit
Python *args und **kwargs erklärt: flexible Funktionen mit klaren Beispielen

Das Wichtigste

  • *args sammelt zusätzliche Positionsargumente in einem Tupel.
  • **kwargs sammelt zusätzliche Schlüsselwortargumente in einem Wörterbuch.
  • Die Namen sind nur eine Konvention, da es auf * und ** ankommt.
  • Dasselbe gilt für das Entpacken einer Liste oder eines Wörterbuchs, wenn Sie eine Funktion aufrufen.

Früher oder später werden Sie eine Funktion wie def handler(*args, **kwargs) lesen und sich fragen, was sie akzeptiert. Python *args und **kwargs ermöglichen einer Funktion die Annahme einer flexiblen Anzahl von Argumenten. Sie unterstützen Dekoratoren, Wrapper und viele Bibliotheks-APIs.

In diesem Handbuch wird beschrieben, wie sie in beide Richtungen funktionieren, zusammen mit den neueren Methoden, mit denen Sie mit Python steuern können, wie eine Funktion aufgerufen wird. Wir haben jedes Beispiel auf Python 3.13 ausgeführt und die angezeigte Ausgabe stammt von diesem Lauf.

Was *args bewirkt

Ein mit einem Stern geschriebener Parameter sammelt alle zusätzlichen Positionsargumente in einem Tupel:

def total(*args):
    print(type(args).__name__, args)
    return sum(args)

print(total(1, 2, 3))
# tuple (1, 2, 3)
# 6

print(total())
# tuple ()
# 0

Ohne Argumente erhalten Sie einfach ein leeres Tupel, sodass die Funktion weiterhin funktioniert. Der Name args ist eine Konvention. *numbers würde sich genauso verhalten, und ein aussagekräftigerer Name ist oft besser.

Was **kwargs macht

Zwei Sterne sammeln alle zusätzlichen Schlüsselwortargumente in einem Wörterbuch:

def describe(**kwargs):
    print(type(kwargs).__name__, kwargs)

describe(name="Asha", city="Pune")
# dict {'name': 'Asha', 'city': 'Pune'}

Da es sich um ein normales Wörterbuch handelt, können Sie es durchlaufen, mit get() nach Schlüsseln suchen und es weitergeben. Schlüssel behalten die Reihenfolge bei, in der sie übergeben wurden.

Parameterreihenfolge

Wenn Sie Arten von Parametern mischen, erfordert Python eine bestimmte Reihenfolge:

  1. Reguläre Positionsparameter
  2. *argumente
  3. Nur-Schlüsselwort-Parameter
  4. **Kwargs
def order(item, qty=1, *extras, priority=False, **notes):
    print(item, qty, extras, priority, notes)

order("pen")
# pen 1 () False {}

order("pen", 2, "gift wrap", "card", priority=True, note="leave at door")
# pen 2 ('gift wrap', 'card') True {'note': 'leave at door'}

Beachten Sie priority. Da es nach *extras kommt, kann es nur über den Namen festgelegt werden, sodass ein verirrtes Positionsargument es niemals versehentlich festlegen könnte. Das frühere Einfügen von **kwargs ist nicht zulässig, und unser Test ergab einen SyntaxError: ​​„Argumente können nicht dem var-keyword-Argument folgen“.

Auspacken beim Anruf

Am Rufort funktionieren die gleichen Sterne umgekehrt. Ein Stern verteilt eine Liste oder ein Tupel in Positionsargumente, und zwei Sterne verteilen ein Wörterbuch in Schlüsselwortargumente:

nums = [4, 5, 6]
total(*nums)            # same as total(4, 5, 6) -> 15

settings = {"name": "Ben", "city": "Delhi"}
describe(**settings)    # same as describe(name="Ben", city="Delhi")

Das Entpacken funktioniert mit jeder Funktion, nicht nur mit denen, die *args und **kwargs verwenden. Das ist praktisch, wenn die Konfiguration als Wörterbuch ankommt:

def connect(host, port, timeout=5):
    return f"{host}:{port} (timeout {timeout})"

connect(**{"host": "db.local", "port": 5432})
# 'db.local:5432 (timeout 5)'

connect(**{"host": "db.local", "port": 5432, "ssl": True})
# TypeError: connect() got an unexpected keyword argument 'ssl'

Dieser Fehler ist nützlich. Eine Funktion ohne **kwargs lehnt unbekannte Optionen ab, sodass ein Tippfehler in einem Einstellungsnamen lautstark fehlschlägt, anstatt stillschweigend ignoriert zu werden.

Nur-Schlüsselwort-Argumente

Eine leere * in der Signatur ohne Namen bedeutet „alles danach muss per Schlüsselwort übergeben werden“:

def make_user(name, *, admin=False):
    return name, admin

make_user("Asha", admin=True)
# ('Asha', True)

make_user("Asha", True)
# TypeError: make_user() takes 1 positional argument but 2 were given

Dies ist eine großartige Möglichkeit, boolesche Flags zu schützen. Ein Aufruf wie make_user("Asha", True) zwingt die Leser dazu, zu erraten, was True bedeutet. Durch die Anforderung von admin=True ist jeder Aufruf selbsterklärend.

Nur positionelle Argumente

Seit Python 3.8 markiert ein / Parameter, die nur nach Position übergeben werden können:

def area(width, height, /):
    return width * height

area(3, 4)                 # 12
area(width=3, height=4)
# TypeError: area() got some positional-only arguments
#   passed as keyword arguments: 'width, height'

Dadurch kann eine Bibliothek diese Parameter später umbenennen, ohne den Code anderer zu beschädigen, da sich niemand auf die Namen verlassen kann. Mehrere integrierte Funktionen funktionieren auf diese Weise. Beispielsweise funktioniert len(obj=[1]) nicht.

Tipp: Das vollständige Muster ist def f(pos_only, /, normal, *, kw_only). Sie benötigen selten alle drei, aber wenn Sie wissen, was / und * bedeuten, können Sie Dokumentation und Bibliothekssignaturen besser lesen.

Dekorateure und Verpackungen

Dies ist in der Praxis der Hauptgrund, beides zusammen zu verwenden. Ein Wrapper muss oft alle Argumente akzeptieren, die die ursprüngliche Funktion annimmt, und sie unverändert weitergeben:

import functools, time

def timed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
        return result
    return wrapper

@timed
def slow_add(a, b, delay=0.01):
    time.sleep(delay)
    return a + b

print(slow_add(2, 3, delay=0.02))
# slow_add took 0.0205s
# 5

Der Wrapper muss die Parameter von slow_add überhaupt nicht kennen, daher funktioniert er für jede Funktion. Ihr Timing-Wert wird geringfügig abweichen. Unser Leitfaden zu Python-Dekoratoren erklärt functools.wraps und andere Details.

Optionen zum Zusammenführen

** funktioniert auch innerhalb eines Wörterbuchliterals, was eine praktische Möglichkeit bietet, Überschreibungen zusätzlich zu den Standardwerten anzuwenden:

defaults = {"theme": "light", "lang": "en"}

def build(**overrides):
    return {**defaults, **overrides}

build(theme="dark")
# {'theme': 'dark', 'lang': 'en'}

Spätere Schlüssel gewinnen, daher ersetzt die Überschreibung die Standardeinstellung. Auch Unterklassen verwenden dieses Muster, wie in super().__init__(*args, **kwargs), um zusätzliche Argumente an eine übergeordnete Klasse weiterzuleiten. Weitere Informationen zu super() finden Sie unter Python-Klassen und -Objekte.

Spickzettel

Syntax Wo Bedeutung
def f(*args) Definition Sammeln Sie zusätzliche Positionsargumente in einem Tupel
def f(**kwargs) Definition Sammeln Sie zusätzliche Schlüsselwortargumente in einem Diktat
def f(a, *, b) Definition b ist nur ein Schlüsselwort
def f(a, /, b) Definition a ist nur positionell
f(*items) Rufen Sie an Verteilen Sie eine Sequenz in Positionsargumente
f(**options) Rufen Sie an Verteilen Sie ein Diktat in Schlüsselwortargumente
{**a, **b} Wörtlich Wörterbücher zusammenführen
[*a, *b] Wörtlich Kombinieren Sie Iterables zu einer Liste

Wann man sie meiden sollte

Flexibilität hat ihren Preis. Eine als def create(**kwargs) definierte Funktion sagt den Lesern nichts darüber, welche Optionen vorhanden sind, und Redakteure können sie nicht automatisch vervollständigen. Tippfehler können unbemerkt mehrere Ebenen durchlaufen, bevor etwas fehlschlägt.

Bevorzugen Sie also explizite Parameter, wenn Sie wissen, was eine Funktion benötigt. Verwenden Sie *args und **kwargs, wenn Sie wirklich Flexibilität benötigen: Wrapper, Dekoratoren, Weiterleitung von Argumenten an eine andere Funktion oder eine echte Eingabe variabler Länge wie total(*numbers). Wenn eine Funktion viele benannte Optionen benötigt, kann eine dataclass auch klarer sein.

Häufige Fehler

Verwendung eines veränderlichen Standardwerts

Hier geht es nicht um Sterne, aber er erscheint oft in deren Nähe. Ein Standardwert wird einmal erstellt, wenn die Funktion definiert wird, und dann zwischen Aufrufen geteilt:

def bad(items=[]):
    items.append(1)
    return items

print(bad(), bad())
# [1, 1] [1, 1]

Beide Aufrufe gaben dieselbe Liste zurück, die zu diesem Zeitpunkt zwei Elemente enthielt. Verwenden Sie items=None und erstellen Sie eine neue Liste innerhalb der Funktion.

Andere Fehler

  • Vergessen auszupacken. Der Aufruf von total(nums) übergibt eine Liste statt drei Nummern. Schreiben Sie total(*nums).
  • Tippfehler werden verschluckt. Eine Funktion, die **kwargs akzeptiert, aber unbekannte Tasten ignoriert, verbirgt falsch geschriebene Optionen. Validieren Sie sie oder vermeiden Sie **kwargs.
  • Das gleiche Argument zweimal übergeben. f(1, **{"a": 2}), wobei a der erste Parameter ist, löst einen Fehler „Mehrere Werte“ aus.
  • Keine Weiterleitung in einem Wrapper. Beim Aufruf von func(args, kwargs) werden ein Tupel und ein Diktat übergeben. Die korrekte Form ist func(*args, **kwargs).

Geben Sie Hinweise für *args und **kwargs ein

Typhinweise zu *args und **kwargs beschreiben jeden einzelnen Wert, nicht die gesamte Sammlung. Das überrascht viele Menschen:

def total(*prices: float) -> float:
    return sum(prices)      # prices is tuple[float, ...]

def tag(**attrs: str) -> str:
    return " ".join(f'{k}="{v}"' for k, v in attrs.items())   # dict[str, str]

total(1.5, 2.5)                  # 4.0
tag(id="x", role="button")       # id="x" role="button"

Hier wird prices als Tupel von Gleitkommazahlen und attrs als Wörterbuch von Zeichenfolgen behandelt. Wenn Schlüsselwortargumente unterschiedliche Typen haben, hat Python 3.12 eine präzise Option hinzugefügt: a TypedDict kombiniert mit Unpack.

from typing import TypedDict, Unpack

class Options(TypedDict, total=False):
    timeout: float
    retries: int

def fetch(url: str, **opts: Unpack[Options]) -> None: ...

Ein Typprüfer wie mypy oder Pyright kann dann fetch(url, retries="3") oder einen falsch geschriebenen Optionsnamen ablehnen, was mit einem einfachen **kwargs niemals möglich ist. Das Laufzeitverhalten ändert sich jedoch nicht. Als wir fetch("u", retries="3") unter Python 3.13 ausgeführt haben, wurde es ohne Beanstandung ausgeführt, da Python selbst keine Typhinweise erzwingt.

Wenn Sie feststellen, dass Sie eine lange Liste akzeptierter Schlüssel für **kwargs dokumentieren, sollten Sie erwägen, diese durch explizite Nur-Schlüsselwort-Parameter zu ersetzen. Redakteure können sie dann automatisch vervollständigen und Fehler werden sofort erkannt.

Fazit

Denken Sie an die beiden Richtungen. In einer Definition sammeln sich Sterne. In einem Anruf packen sie aus. Fügen Sie Nur-Schlüsselwort-Argumente hinzu, um Aufrufe klarer zu gestalten, und reservieren Sie **kwargs für Code, der Argumente tatsächlich weiterleitet. Schreiben Sie zum Üben einen kleinen Dekorator, der die Argumente jedes Aufrufs protokolliert, und verwenden Sie ihn für einige verschiedene Funktionen.

Häufige Fragen

Was ist der Unterschied zwischen *args und **kwargs?

*args sammelt zusätzliche Positionsargumente in einem Tupel. **kwargs sammelt zusätzliche Schlüsselwortargumente in einem Wörterbuch. Eine Funktion kann beides zusammen verwenden.

Muss ich sie args und kwargs nennen?

Nein. Die Sterne erledigen die Arbeit, daher sind *Werte und **Optionen gleichermaßen gültig. args und kwargs sind einfach eine weithin anerkannte Konvention.

In welcher Reihenfolge sollten *args und **kwargs stehen?

Zuerst reguläre Parameter, dann *args, dann Nur-Schlüsselwort-Parameter und zuletzt **kwargs. Jede andere Reihenfolge führt zu einem SyntaxError.

Was bedeutet ein einzelnes * in einer Funktionsdefinition?

Ein bloßes * bedeutet, dass jeder Parameter danach als Schlüsselwort übergeben werden muss. Es macht Aufrufe klarer, insbesondere für boolesche Flags.

Was bedeutet / bedeutet in einer Python-Funktion?

Parameter vor / sind nur positionell, sodass Aufrufer sie nicht namentlich übergeben können. Diese Funktion wurde in Python 3.8 hinzugefügt.

Lerne weiter

Arbeiten Sie mit Stack Quora

Benötigen Sie Hilfe beim Testen, WordPress oder SEO?

Wir helfen Teams, bessere Software zu liefern und ihre Websites zu erweitern. Sagen Sie uns, woran Sie gerade arbeiten, und wir schlagen Ihnen einen klaren nächsten Schritt vor.

Lesen Sie weiter

Weitere Anleitungen finden Sie unter Tests, KI und moderne Entwicklung

Hat Ihnen dieser Artikel gefallen? Wählen Sie ein Thema und springen Sie direkt in unsere neuesten Tutorials.

Künstliche Intelligenz

KI-Halluzinationen erklärt: Warum Sprachmodelle Dinge erfinden und wie man sie reduziert

KI-Halluzinationen erklärt: Was sie sind, warum Sprachmodelle sie produzieren, gängige Typen, wie man sie mit Erdung und Eingabeaufforderungen reduz…

Artikel lesen →
DevOps

Erklärte Umgebungsvariablen: .env-Dateien, Geheimnisse und Konfiguration richtig gemacht

Umgebungsvariablen anhand von Python-Beispielen erklärt, die wir ausgeführt haben: Sie lesen und festlegen, warum jeder Wert ein String ist, .env-Da…

Artikel lesen →
Softwareentwicklung

API-Ratenbegrenzung erklärt: Algorithmen, Header und Behandlung von 429-Fehlern

API-Ratenbegrenzung anhand einer von uns durchgeführten Simulation erklärt: Token-Bucket, festes Fenster, gleitendes Fenster, 429 Antworten, Ratenbe…

Artikel lesen →
Testautomatisierung

Spott in Python: unittest.mock, Patch und die Fehler, die Tests zum Lügen machen

Mocking in Python mit unittest.mock, getestet: Mock und return_value, side_effect, Patchen, wo ein Name verwendet wird, Spezifikation und Autospec, …

Artikel lesen →
Softwaretests

Code-Abdeckung erklärt: Leitungs- und Zweigstellenabdeckung und was 100 % wirklich bedeutet

Erklärung der Codeabdeckung anhand eines echten Coverage.py-Laufs: Zeilen-, Zweig- und Bedingungsabdeckung, Lesen eines Berichts, warum bei 100 % Ab…

Artikel lesen →
Datenbanken & SQL

DELETE vs. TRUNCATE vs. DROP in SQL: Unterschiede, Rollback und Verwendungszweck

DELETE vs. TRUNCATE vs. DROP erklärt mit getestetem SQL: was jeweils entfernt wird, Rollback, Trigger, Identitätsrücksetzung, Geschwindigkeit und wi…

Artikel lesen →
Datenbanken & SQL

Erklärte SQL-Ansichten: Erstellen, nutzen und vermeiden Sie Fallstricke mit Beispielen

SQL-Ansichten anhand der von uns ausgeführten Abfragen erklärt: Erstellen einer Ansicht, Live-Ergebnisse, Ausblenden von Spalten, Leistung, aktualis…

Artikel lesen →
Softwareentwicklung

TypeScript-Schnittstelle vs. Typ: Echte Unterschiede, getestet mit dem Compiler

TypeScript-Schnittstelle vs. Typ, überprüft mit dem echten Compiler: Was beide können, was nur einer kann, Deklarationszusammenführung, Erweiterunge…

Artikel lesen →
Softwareentwicklung

Python *args und **kwargs erklärt: Flexible Funktionen mit klaren Beispielen

Python *args und **kwargs erklärt mit Code, den wir ausgeführt haben: Packen und Entpacken, Parameterreihenfolge, Nur-Schlüsselwort- und Nur-Positio…

Artikel lesen →
Softwareentwicklung

Python-Lambda-Funktionen: Syntax, tatsächliche Verwendung und wann man sie nicht verwendet

Python-Lambda-Funktionen erklärt mit Code, den wir ausgeführt haben: Syntax, Sortierung mit Schlüssel, Karte und Filter, Abschlüsse, die Late-Bindin…

Artikel lesen →
Künstliche Intelligenz

Was sind KI-Agenten? Wie sie funktionieren, mit einer einfachen Agentenschleife, die Sie ausführen können

Was sind KI-Agenten? Eine einfache Anleitung zur Agentenschleife, zu Tools, Speicher und Leitplanken, mit einer kleinen Python-Schleife, die wir aus…

Artikel lesen →
DevOps

Überwachung vs. Beobachtbarkeit: Der wahre Unterschied, mit Protokollen, Metriken und Traces

Überwachung vs. Beobachtbarkeit erklärt: bekannte und unbekannte Fehler, Protokolle, Metriken und Spuren, eine exemplarische Vorgehensweise für Chec…

Artikel lesen →
DevOps

Docker-Volumes vs. Bind Mounts: Containerdaten auf die richtige Art und Weise beibehalten

Docker-Volumes vs. Bind-Mounts erklärt: Warum Containerdaten verschwinden, benannte Volumes, Bind-Mounts, tmpfs, Compose-Beispiele, Backups, Berecht…

Artikel lesen →
Testautomatisierung

TDD vs. BDD: Hauptunterschiede, eine Funktion, die auf beide Arten entwickelt wurde und wann sie jeweils verwendet werden sollte

TDD vs. BDD erklärt mit einer Rabattfunktion, die in beide Richtungen aufgebaut ist: Rot-Grün-Refaktor mit Code, den wir ausgeführt haben, Gegeben-W…

Artikel lesen →
Softwareentwicklung

Git merge vs rebase: Unterschiede, Beispiele und wann man was nutzt

Git merge vs rebase erklärt mit echter Log-Ausgabe, einer Vergleichstabelle, der goldenen Regel des Rebase und einem sicheren Team-Workflow.

Artikel lesen →
Softwareentwicklung

Python List Comprehension: Syntax, Beispiele und wann man darauf verzichtet

Python List Comprehension erklärt: Syntax, Filter, if-else, verschachtelte Schleifen, gemessene Zeiten und Fälle, in denen eine normale for-Schleife…

Artikel lesen →
Datenbanken & SQL

SQL Joins erklärt mit Beispielen (Inner, Left, Right, Full)

SQL Joins Schritt für Schritt mit Beispieltabellen und Abfragen verstehen: INNER, LEFT, RIGHT, FULL, CROSS und SELF, plus typische Fehler und Perfor…

Artikel lesen →