Function Arguments Interview Questions & Answers
6 questions Updated 2026-06-18
Python interview questions on args and kwargs, positional vs keyword arguments, defaults, keyword-only and positional-only parameters, unpacking at the call site, and parameter ordering rules.
In a function signature, *args collects extra positional arguments
into a tuple, and **kwargs collects extra keyword arguments into a
dict. They let a function accept a variable number of arguments. The names
are convention — only the */** matter.
def log(level, *args, **kwargs):
print(level, args, kwargs)
log("INFO", 1, 2, user="ada", id=7)
# INFO (1, 2) {'user': 'ada', 'id': 7}
args is always a tuple and kwargs always a dict. They're essential
for writing wrappers/decorators that forward arbitrary arguments through to
another callable.
Positional arguments are matched to parameters by their order.
Keyword arguments are matched by name (param=value), so order
doesn't matter among them. At the call site you can mix the two, but every
positional argument must come before any keyword argument.
def greet(name, greeting): ...
greet("Ada", "Hello") # both positional
greet(name="Ada", greeting="Hi") # both keyword (order free)
greet("Ada", greeting="Hi") # mix: positional first
greet(name="Ada", "Hi") # SyntaxError — kw before positional
Keyword arguments make calls self-documenting and let you skip over earlier parameters that have defaults. Use them for clarity on boolean flags and long argument lists.
A parameter with name=value is optional — if the caller omits it, the
default is used. Defaults are evaluated once, when the def runs, so
using a mutable default ([], {}) is a classic trap: the same object
persists across calls.
def connect(host, port=5432, timeout=30):
...
connect("db") # uses port=5432, timeout=30
connect("db", timeout=5) # override one by keyword
def bad(item, bucket=[]): # DON'T — shared list
bucket.append(item); return bucket
The safe pattern for a mutable default is bucket=None plus
if bucket is None: bucket = [] inside the body. Parameters with defaults
must come after those without.
Any parameter listed after a bare * (or after *args) is
keyword-only — it can never be passed positionally and must be named at
the call site. This forces clearer calls and prevents accidental
positional mistakes.
def make_request(url, *, timeout=30, verify=True):
...
make_request("http://x", timeout=5) # OK
make_request("http://x", 5) # TypeError — timeout is kw-only
Keyword-only parameters are great for optional flags whose meaning isn't
obvious from position (especially booleans). The lone * is just a
separator; it doesn't collect anything.
Parameters listed before a / in the signature are positional-only
(Python 3.8+) — they cannot be passed by keyword. This is useful for APIs
where the parameter name is an implementation detail you don't want callers
to depend on.
def divide(a, b, /):
return a / b
divide(10, 2) # OK
divide(a=10, b=2) # TypeError — a, b are positional-only
It also frees those names for use in **kwargs. Many built-ins (like
len, pow) are positional-only. Combined with *, a signature can have
positional-only, normal, and keyword-only sections.
A full signature follows a fixed order:
positional-only /, then normal, then *args, then keyword-only, then
**kwargs. Within each group, parameters without defaults precede those
with defaults.
def f(pos_only, /, normal, *args, kw_only, **kwargs):
...
# call-site unpacking mirrors this:
def g(a, b, c): ...
nums = [1, 2, 3]
g(*nums) # spread list into positionals
g(**{"a": 1, "b": 2, "c": 3}) # spread dict into keywords
Getting the order wrong is a SyntaxError. The *// markers partition
the signature; remember the sequence "positional-only → normal → varargs →
keyword-only → varkwargs."
Practice tests are coming soon
Get notified when interactive mock interviews and quizzes launch.