Python *args en **kwargs uitgelegd: flexibele functies met duidelijke voorbeelden

  1. Home
  2. Blog
  3. Softwareontwikkeling

Softwareontwikkeling

Python *args en **kwargs uitgelegd: Flexibele functies met duidelijke voorbeelden

Die sterren in functiesignaturen zijn minder mysterieus dan ze lijken. Leer hoe *args en **kwargs argumenten verzamelen, hoe dezelfde sterren ze uitpakken en hoe u functiesignaturen ontwerpt die moeilijk te misbruiken zijn.

8 min leestijd
Python *args en **kwargs uitgelegd: flexibele functies met duidelijke voorbeelden

Belangrijkste punten

  • *args verzamelt extra positionele argumenten in een tupel.
  • **kwargs verzamelt extra trefwoordargumenten in een woordenboek.
  • De namen zijn slechts een conventie, aangezien de * en ** er toe doen.
  • Dezelfde sterren pakken een lijst of woordenboek uit wanneer je een functie aanroept.

Vroeg of laat zul je een functie als def handler(*args, **kwargs) lezen en je afvragen wat deze accepteert. Python *args en **kwargs zorgen ervoor dat een functie een flexibel aantal argumenten kan aannemen. Ze voeden decorateurs, wrappers en vele bibliotheek-API’s.

In deze handleiding wordt beschreven hoe ze in beide richtingen werken, samen met de nieuwere manieren waarop je met Python kunt bepalen hoe een functie wordt aangeroepen. We hebben elk voorbeeld uitgevoerd op Python 3.13 en de getoonde uitvoer is afkomstig van die run.

Wat *args doet

Een parameter geschreven met één ster verzamelt eventuele extra positionele argumenten in een 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

Zonder argumenten krijg je eenvoudigweg een leeg tupel, zodat de functie nog steeds werkt. De naam args is een conventie. *numbers zou zich hetzelfde gedragen, en een meer beschrijvende naam is vaak beter.

Wat **kwargs doet

Twee sterren verzamelen eventuele extra trefwoordargumenten in een woordenboek:

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

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

Omdat het een normaal woordenboek is, kun je er doorheen lopen, sleutels opzoeken met get() en doorgeven. Sleutels behouden de volgorde waarin ze zijn doorgegeven.

Parametervolgorde

Wanneer je verschillende soorten parameters combineert, heeft Python een specifieke volgorde nodig:

  1. Reguliere positionele parameters
  2. *argumenten
  3. Parameters voor alleen zoekwoorden
  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'}

Let op priority. Omdat het na *extras komt, kan het alleen op naam worden ingesteld, dus een verdwaald positioneel argument kan het nooit per ongeluk instellen. Het is niet toegestaan ​​om **kwargs eerder te plaatsen, en onze test leverde een SyntaxError op: “argumenten kunnen het var-keyword-argument niet volgen”.

Uitpakken tijdens het bellen

Dezelfde sterren werken omgekeerd op de call-site. Eén ster verspreidt een lijst of tupel in positionele argumenten, en twee sterren spreiden een woordenboek uit in trefwoordargumenten:

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")

Uitpakken werkt met elke functie, niet alleen degene die *args en **kwargs gebruiken. Dat is handig als de configuratie als woordenboek arriveert:

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'

Die fout is nuttig. Een functie zonder **kwargs wijst onbekende opties af, dus een typefout in de naam van een instelling mislukt luid in plaats van stil te worden genegeerd.

Argumenten met alleen trefwoorden

Een kale * in de handtekening, zonder naam, betekent “alles hierna moet per trefwoord worden doorgegeven”:

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

Dit is een geweldige manier om Booleaanse vlaggen te beschermen. Een oproep als make_user("Asha", True) dwingt lezers om te raden wat True betekent. Door admin=True te vereisen, spreekt elk gesprek voor zich.

Alleen positionele argumenten

Sinds Python 3.8 markeert een / parameters die alleen per positie kunnen worden doorgegeven:

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'

Hierdoor kan een bibliotheek deze parameters later hernoemen zonder iemands code te breken, aangezien niemand op de namen kan vertrouwen. Verschillende ingebouwde programma’s werken op deze manier. len(obj=[1]) werkt bijvoorbeeld niet.

Tip: Het volledige patroon is def f(pos_only, /, normal, *, kw_only). Je hebt ze zelden alle drie nodig, maar als je weet wat / en * betekenen, kun je documentatie en bibliotheekhandtekeningen lezen.

Decorateurs en inpakkers

Dit is de belangrijkste reden in de echte wereld om beide samen te gebruiken. Een wrapper moet vaak alle argumenten accepteren die de oorspronkelijke functie aanneemt, en deze onveranderd doorgeven:

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

De wrapper hoeft de parameters van slow_add helemaal niet te kennen, dus hij werkt voor elke functie. Uw timingcijfer zal enigszins afwijken. In onze gids voor Python-decorateurs worden functools.wraps en andere details uitgelegd.

Opties samenvoegen

** werkt ook binnen een letterlijk woordenboek, wat een handige manier biedt om overschrijvingen toe te passen bovenop de standaardwaarden:

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

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

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

Latere sleutels winnen, dus de overschrijving vervangt de standaardsleutel. Subklassen gebruiken dit patroon ook, zoals in super().__init__(*args, **kwargs), om extra argumenten door te geven aan een ouderklasse. Zie Python-klassen en -objecten voor meer informatie over super().

Spiekbriefje

Syntaxis Waar Betekenis
def f(*args) Definitie Verzamel extra positionele argumenten in een tupel
def f(** kwargs) Definitie Verzamel extra trefwoordargumenten in een dictaat
def f(a, *, b) Definitie b is alleen voor zoekwoorden
def f(a, /, b) Definitie a is alleen positioneel
f(*items) Bel Verdeel een reeks in positionele argumenten
f(**options) Bel Verdeel een dictaat in trefwoordargumenten
{**a, **b} Letterlijk Woordenboeken samenvoegen
[*a, *b] Letterlijk Combineer iterabelen in een lijst

Wanneer moet u ze vermijden?

Flexibiliteit heeft een prijs. Een functie gedefinieerd als def create(**kwargs) vertelt lezers niets over welke opties er zijn, en editors kunnen deze niet automatisch aanvullen. Typefouten kunnen geruisloos door verschillende lagen gaan voordat er iets misgaat.

Geef dus de voorkeur aan expliciete parameters als je weet wat een functie nodig heeft. Gebruik *args en **kwargs als je echt flexibiliteit nodig hebt: wrappers, decorateurs, argumenten doorsturen naar een andere functie, of een echte invoer met variabele lengte zoals total(*numbers). Als een functie veel benoemde opties nodig heeft, kan een dataclass ook duidelijker zijn.

Veel voorkomende fouten

Een veranderlijke standaard gebruiken

Dit gaat niet over sterren, maar het verschijnt vaak dichtbij hen. Er wordt één keer een standaardwaarde gemaakt wanneer de functie wordt gedefinieerd, en vervolgens gedeeld tussen aanroepen:

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

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

Beide oproepen leverden precies dezelfde lijst op, die tegen die tijd twee items bevatte. Gebruik items=None en maak een nieuwe lijst binnen de functie.

Andere fouten

  • Vergeten uit te pakken. Als u total(nums) belt, wordt er één lijst doorgegeven in plaats van drie nummers. Schrijf total(*nums).
  • Typefouten inslikken. Een functie die **kwargs accepteert maar onbekende sleutels negeert, verbergt verkeerd gespelde opties. Valideer ze, of vermijd **kwargs.
  • Als hetzelfde argument twee keer wordt doorgegeven. f(1, **{"a": 2}), waarbij a de eerste parameter is, ontstaat er een fout met ‘meerdere waarden’.
  • Niet doorsturen in een wrapper. Bij het aanroepen van func(args, kwargs) wordt een tuple en een dictaat doorgegeven. De juiste vorm is func(*args, **kwargs).

Typ hints voor *args en **kwargs

Typehints op *args en **kwargs beschrijven elke individuele waarde, niet de hele verzameling. Dit verbaast veel mensen:

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 wordt prices behandeld als een tupel van drijvers en attrs als een woordenboek van strings. Wanneer trefwoordargumenten verschillende typen hebben, heeft Python 3.12 een precieze optie toegevoegd: a TypedDict gecombineerd met Unpack.

from typing import TypedDict, Unpack

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

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

Een typecontrole zoals mypy of Pyright kan vervolgens fetch(url, retries="3") of een verkeerd gespelde optienaam weigeren, wat een gewone **kwargs nooit kan doen. Het runtime-gedrag verandert echter niet. Toen we fetch("u", retries="3") uitvoerden onder Python 3.13, werd het zonder klachten uitgevoerd, omdat Python zelf geen typehints afdwingt.

Als je merkt dat je een lange lijst met geaccepteerde sleutels voor **kwargs documenteert, overweeg dan om deze te vervangen door expliciete parameters die alleen uit trefwoorden bestaan. Editors kunnen ze vervolgens automatisch aanvullen en fouten worden onmiddellijk opgemerkt.

Tot slot

Onthoud de twee richtingen. In een definitie verzamelen sterren zich. Tijdens een gesprek pakken ze uit. Voeg alleen-trefwoord-argumenten toe om aanroepen duidelijker te maken, en reserveer **kwargs voor code die argumenten daadwerkelijk doorstuurt. Om te oefenen kunt u een kleine decorateur schrijven die de argumenten van elke oproep registreert, en deze voor een paar verschillende functies gebruiken.

Veelgestelde vragen

Wat is het verschil tussen *args en **kwargs?

*args verzamelt extra positionele argumenten in een tuple. **kwargs verzamelt extra trefwoordargumenten in een woordenboek. Een functie kan beide samen gebruiken.

Moet ik ze args en kwargs noemen?

Nee. De sterren doen het werk, dus *waarden of **opties zijn even geldig. args en kwargs zijn eenvoudigweg een algemeen erkende conventie.

In welke volgorde moeten *args en **kwargs staan?

Eerst de reguliere parameters, daarna *args, dan de parameters voor alleen trefwoorden, en **kwargs als laatste. Elke andere volgorde veroorzaakt een SyntaxError.

Wat betekent een enkele * in een functiedefinitie?

Een kale * betekent dat elke parameter erna per trefwoord moet worden doorgegeven. Het maakt oproepen duidelijker, vooral voor Booleaanse vlaggen.

Wat betekent / betekent in een Python-functie?

Parameters vóór / zijn alleen positioneel, dus bellers kunnen ze niet op naam doorgeven. Deze functie is toegevoegd in Python 3.8.

Blijf leren

Werk met Stack Quora

Hulp nodig met testen, WordPress of SEO?

We helpen teams betere software te leveren en hun sites te laten groeien. Vertel ons waar u mee bezig bent en wij stellen een duidelijke volgende stap voor.

Blijf lezen

Meer handleidingen over testen, AI en moderne ontwikkeling

Genoten van dit artikel? Kies een onderwerp en ga direct naar onze nieuwste tutorials.

Kunstmatige intelligentie

AI-hallucinaties uitgelegd: waarom taalmodellen dingen verzinnen en hoe ze te verminderen

AI-hallucinaties uitgelegd: wat ze zijn, waarom taalmodellen ze produceren, veelvoorkomende typen, hoe je ze kunt verminderen met aarding en aanwijz…

Lees artikel →
DevOps

Omgevingsvariabelen uitgelegd: .env-bestanden, geheimen en configuratie goed gedaan

Omgevingsvariabelen uitgelegd met Python-voorbeelden die we hebben uitgevoerd: ze lezen en instellen, waarom elke waarde een string is, .env-bestand…

Lees artikel →
Softwareontwikkeling

API-snelheidsbeperking uitgelegd: algoritmen, headers en afhandeling van 429-fouten

API-snelheidsbeperking uitgelegd met een simulatie die we hebben uitgevoerd: token-bucket, vast venster, schuifvenster, 429 reacties, headers van sn…

Lees artikel →
Testautomatisering

Spotten in Python: unittest.mock, patch en de fouten die tests doen liegen

Spotten in Python met unittest.mock, getest: Mock en return_value, side_effect, patchen waar een naam wordt gebruikt, spec en autospec, spotdatums e…

Lees artikel →
Softwaretesten

Codedekking uitgelegd: lijn versus filiaaldekking en wat 100% werkelijk betekent

Codedekking uitgelegd met een echte dekking.py-run: dekking van lijnen, vertakkingen en condities, een rapport lezen, waarom 100% dekking nog steeds…

Lees artikel →
Databases & SQL

DELETE versus TRUNCATE versus DROP in SQL: verschillen, terugdraaien en wanneer elk te gebruiken

DELETE versus TRUNCATE versus DROP uitgelegd met geteste SQL: wat elk verwijdert, terugdraaien, triggers, identiteitsreset, snelheid en hoe PostgreS…

Lees artikel →
Databases & SQL

SQL-weergaven uitgelegd: creëer, gebruik en vermijd de valkuilen, met voorbeelden

SQL-weergaven uitgelegd met query's die we hebben uitgevoerd: een weergave maken, live resultaten, kolommen verbergen, prestaties, bijwerkbare en ge…

Lees artikel →
Softwareontwikkeling

TypeScript-interface versus type: echte verschillen, getest met de compiler

TypeScript-interface versus type, gecontroleerd met de echte compiler: wat beide kunnen doen, wat slechts één kan doen, samenvoegen van declaraties,…

Lees artikel →
Softwareontwikkeling

Python *args en **kwargs uitgelegd: flexibele functies met duidelijke voorbeelden

Python *args en **kwargs uitgelegd met code die we hebben uitgevoerd: in- en uitpakken, parametervolgorde, argumenten die alleen trefwoorden en posi…

Lees artikel →
Softwareontwikkeling

Python Lambda-functies: syntaxis, echt gebruik en wanneer u ze niet moet gebruiken

Python lambda-functies uitgelegd met code die we hebben uitgevoerd: syntaxis, sorteren met sleutel, kaart en filter, sluitingen, de laat-bindende va…

Lees artikel →
Kunstmatige intelligentie

Wat zijn AI-agenten? Hoe ze werken, met een eenvoudige agentloop die u kunt uitvoeren

Wat zijn AI-agenten? Een duidelijke gids voor de agentenlus, tools, geheugen en vangrails, met een kleine Python-lus die we hebben uitgevoerd, echte…

Lees artikel →
DevOps

Monitoring versus waarneembaarheid: het echte verschil, met logboeken, statistieken en sporen

Monitoring versus waarneembaarheid uitgelegd: bekende en onbekende fouten, logboeken, statistieken en sporen, een walkthrough voor afrekenincidenten…

Lees artikel →
DevOps

Docker-volumes versus bindingsmounts: bewaar containergegevens op de juiste manier

Docker-volumes versus bind-mounts uitgelegd: waarom containergegevens verdwijnen, benoemde volumes, bind-mounts, tmpfs, Compose-voorbeelden, back-up…

Lees artikel →
Testautomatisering

TDD versus BDD: belangrijkste verschillen, één functie die op beide manieren is gebouwd en wanneer deze moet worden gebruikt

TDD versus BDD uitgelegd met één kortingsfunctie die in beide richtingen is gebouwd: rood-groen-refactor met code die we hebben uitgevoerd, gegeven-…

Lees artikel →
Softwareontwikkeling

Git merge vs rebase: verschillen, voorbeelden en wanneer je wat gebruikt

Git merge vs rebase uitgelegd met echte logoutput, een vergelijkingstabel, de gouden regel van rebase en een veilige teamwerkwijze.

Lees artikel →
Softwareontwikkeling

Python list comprehension: syntaxis, voorbeelden en wanneer je het vermijdt

Python list comprehension uitgelegd: syntaxis, filteren, if-else, geneste lussen, gemeten tijden en wanneer een gewone for-lus beter is.

Lees artikel →
Databases & SQL

SQL JOIN uitgelegd met voorbeelden (Inner, Left, Right, Full)

Begrijp SQL joins stap voor stap met voorbeeldtabellen en queries: INNER, LEFT, RIGHT, FULL, CROSS en SELF, plus veelgemaakte fouten en prestatietips.

Lees artikel →