What are *args and **kwargs in Python?
In a function definition, *args collects any extra positional arguments into a tuple, and **kwargs collects any extra keyword arguments into a dict. They let a function accept a variable number of inputs. The same * and ** symbols also unpack a list or dict when calling a function.
Explain it simply
Imagine a college fest registration desk. Some students come alone, some bring three friends. The desk does not have a fixed number of chairs; it just says "all of you, sit in this row". *args is that row: however many positional values you pass, they are collected together.
**kwargs is like a form with optional fields. One student fills "college" and "phone", another fills only "email". Whatever named fields arrive are collected into a dictionary.
The names args and kwargs are only a convention. The stars do the work: *items or **options would behave the same way.
Example
def total_bill(*amounts, **extras):
print(amounts) # a tuple
print(extras) # a dict
subtotal = sum(amounts)
tip = extras.get("tip", 0)
return subtotal + tip
print(total_bill(120, 80, 45))
# (120, 80, 45)
# {}
# 245
print(total_bill(200, 150, tip=30, coupon="FEST10"))
# (200, 150)
# {'tip': 30, 'coupon': 'FEST10'}
# 380Unpacking at the call site works the other way round:
prices = [120, 80, 45]
options = {"tip": 20}
print(total_bill(*prices, **options)) # same as total_bill(120, 80, 45, tip=20) -> 265How it works
Python matches arguments in a fixed order. In a definition, parameters go like this:
def f(a, b=2, *args, c, d=4, **kwargs):
...a,b: normal positional-or-keyword parameters.*args: any remaining positional values, as a tuple.c,d: keyword-only parameters, because they come after*args. They must be passed by name.**kwargs: any remaining named values, as a dict.
A bare * without a name (def f(a, *, c)) also makes the following parameters keyword-only, without collecting extras.
Real-world use
- Wrappers and decorators: a decorator does not know the wrapped function's signature, so it accepts
*args, **kwargsand passes them straight through. - Calling parent classes:
super().__init__(*args, **kwargs)in Django models and forms. - Flexible helpers: a logging function that takes any number of values, like
printitself. - Passing settings: building a dict of options and calling
requests.get(url, **options).
Common mistakes
- Putting
**kwargsbefore*argsin the definition. That is a syntax error; the order is fixed. - Forgetting that
argsis a tuple, so you cannotappendto it. - Overusing them. If a function always needs
nameandage, list them explicitly. Clear signatures are easier to read and give better error messages. - Passing the same argument twice, like
f(1, a=1), which raisesTypeError: got multiple values for argument 'a'.
What to say in the interview
"*args lets a function take any number of extra positional arguments, which arrive as a tuple. **kwargs takes any number of extra keyword arguments, which arrive as a dict. The order in a definition is normal parameters, then *args, then keyword-only parameters, then **kwargs. I mostly use them in decorators and wrappers, where I need to accept whatever the wrapped function takes and pass it through unchanged. At the call site, * and ** do the reverse and unpack a list or dict into arguments."
Follow-up questions
- What is a keyword-only argument, and how do you create one?
- What does
/mean in a function signature likedef f(a, b, /, c)? - How would you write a decorator that works with any function?
- Can you merge two dictionaries using
**?
Go deeper
Read these once the basics above feel easy.
Since Python 3.8, a / in the parameter list marks everything before it as positional-only. For example, in def f(a, b, /, c), you cannot call f(a=1, b=2, c=3). Many built-ins work this way, such as len(obj).
** also works inside dict literals: {**defaults, **user_settings} merges two dicts, with later keys winning. Python 3.9 added the | operator for the same job: defaults | user_settings.