Functions, arguments, and *args / **kwargs
Positional against keyword arguments, star-unpacking, and what a decorator actually is.
After this lesson you can
- Tell positional-only, keyword-only, and either-way parameters apart
- Use *args and **kwargs to accept an arbitrary number of arguments
- Explain what a decorator does to the function underneath it
A parameter can usually be filled by position or by name.
def greet(name, greeting="Hello"):
return f"{greeting}, {name}"
greet("Nino") # "Hello, Nino"
greet("Nino", "Hi") # "Hi, Nino"
greet(name="Nino", greeting="Hi") # "Hi, Nino" — same call, by keyword
def greet(name, /, greeting) forces name to be positional-only — the
caller cannot write greet(name="Nino", ...). def greet(*, greeting)
forces greeting to be keyword-only. Both exist to make an API's
intent explicit rather than leaving it to convention.
*args and **kwargs
*args collects any number of extra positional arguments into a tuple.
**kwargs collects any number of extra keyword arguments into a dict.
def total(*args, **kwargs):
return sum(args) + sum(kwargs.values())
total(1, 2, 3, extra=10) # 16
The names args and kwargs are convention, not syntax — the * and
** are what matter. The same stars unpack in the other direction, at a
call site:
nums = [1, 2, 3]
print(*nums) # print(1, 2, 3) — three separate arguments
opts = {"greeting": "Hi"}
greet("Nino", **opts) # greet(name="Nino", greeting="Hi")
Try it
def total(*args, **kwargs): return sum(args) + sum(kwargs.values()) def run(): return { "just_args": total(1, 2, 3), "with_bonus": total(1, 2, bonus=10, extra=5), }Closures over loop-bound state
A function defined inside another function keeps access to the
enclosing function's variables even after the outer one has returned.
That has a trap in a loop (the one JavaScript has with var):
def make_multipliers():
return [lambda x: x * i for i in range(3)]
fns = make_multipliers()
[f(10) for f in fns] # [20, 20, 20] — every lambda shares the same i
Python does not make a fresh i for each iteration; all three lambdas
close over the same i, and by the
time any of them run the loop has finished with i at 2. The fix is
to capture the value as a default argument, evaluated once per lambda at
definition time:
[lambda x, i=i: x * i for i in range(3)] # [0, 10, 20]
Decorators wrap a function, they do not change its source
def log_calls(fn):
def wrapper(*args, **kwargs):
print(f"calling {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
@log_calls above add is exactly add = log_calls(add). wrapper
is what add actually refers to afterwards; it accepts anything via
*args, **kwargs and forwards it, which is why a decorator can wrap a
function it knows nothing about the signature of. The decorators lesson
later in this course takes this further.
def count(*args): return len(args) def shout(fn): def wrapper(*args, **kwargs): fn(*args, **kwargs) # the result is dropped: no return return wrapper @shoutdef double(x): return x * 2 def run(): late = [lambda x: x * i for i in range(3)] bound = [lambda x, i=i: x * i for i in range(3)] nums = [4, 5, 6] return { "late": [f(10) for f in late], "bound": [f(10) for f in bound], "count_of_list": count(nums), "count_unpacked": count(*nums), "decorated_double": double(21), }What interviewers ask
- What do
*argsand**kwargsreceive? A tuple of the extra positional arguments and a dict of the extra keyword arguments; the same stars unpack a sequence or a mapping at a call site. - What does
@decoratorabove adefactually do? It runs once, at definition time, and rebinds the name:f = decorator(f). - Why do these lambdas made in a loop all return the same thing?
Closures capture variables, not values, and look them up when called;
bind the value with a default argument or
functools.partial.
Try it yourself
2 visible tests · 2 hidden testsImplement entryPoint(*args). args may hold any number of numbers,
including none. Return a dict with "count" (how many arguments came
in) and "total" (their sum, 0 for none).
entryPoint(1, 2, 3)entryPoint(42)
Sign up to run the tests, check the hidden ones and save your progress. Sign up