ProgrammingPython Developer

Explain how *args and **kwargs work in Python functions. What are they used for, what are the risks of improper use, and how should they be correctly combined with regular and named arguments?

Pass interviews with Hintsage AI assistant

Answer

*args collects an arbitrary number of positional arguments into a tuple.

**kwargs collects an arbitrary number of named arguments (key=value) into a dictionary.

This allows writing functions with a flexible interface:

def my_func(a, b, *args, **kwargs): print(a, b) print(args) print(kwargs) my_func(1, 2, 3, 4, x=10, y=20) # 1 2 # (3, 4) # {'x': 10, 'y': 20}

Nuances:

  • Regular arguments come first, then *args, followed by named arguments with default values, and finally **kwargs — strictly in this order when declaring a function.
  • You can "unpack" sequences with * when calling a function, and dictionaries with **.
  • You can't redefine the same parameters — a TypeError will occur.

Trick Question

Can the order of *args and named arguments with default values be changed, for example:

def foo(a, *args, x=10, **kwargs): pass

Or must default values always be placed after *args?

Answer:

In Python 3, yes! Like this (it didn't work in Python 2):

def foo(a, *args, x=10): pass

Parameters after *args ("keyword-only arguments") can have default values or be required — they can only be specified by name.

Examples of real errors due to ignorance of the nuances of the topic


Story

Passing incorrect arguments through *args/**kwargs

In REST API services, data marshalling automation was built through **kwargs. An extra parameter went unnoticed — the function accepted an unexpected value in kwargs, leading to loss of control logic and difficult-to-debug bugs.


Story

Duplicating named arguments

When calling a function, a developer accidentally specified a parameter both explicitly and through **kwargs, for example: my_func(a=1, **{"a": 2}). As a result — a TypeError and service crash.


Story

Forgotten positional or named-only arguments

When writing a decorator, incorrect parameter passing (order and structure were not followed) meant that the original function received an incorrect number of arguments, the agreements of which changed, and TypeError call errors began to occur in production.