Mental model: Think of
*args and **kwargs as two open boxes at a reception desk. One box accepts loose items (positional values) in the order they arrive; the other has labelled slots for named data. You don't need to know how many guests are coming or what their names will be—both boxes just fill up.Why this matters: Real programs often receive variable-length inputs: a calculator that adds any number of expenses, a logger that tags events with an arbitrary set of metadata keys, or a config merger that combines settings from multiple sources. Fixed parameters force you to write
sum_2, sum_3, etc., which is brittle and repetitive.*args: Any positional arguments
def sum_all(*nums):
total = 0
for n in nums:
total += n
return total
print(sum_all(1, 2)) # 3
print(sum_all(5)) # 5
print(sum_all()) # 0args is just a name (the example above uses nums). Python packs all extra positional arguments into the variable after the star as a tuple. You can loop over it, index it, or pass it to built-ins like len() and sum(). The star tells Python: "gather everything here instead of checking counts."**kwargs: Any keyword arguments
def describe(**info):
for key, value in info.items():
print(f"{key}: {value}")
describe(name='Ada', role='engineer')**kwargs gathers all named arguments that do not match an explicit parameter. They arrive as a dict, so for key, value in kwargs.items() is the usual pattern. The double star signals: "collect everything with names into this dictionary."