*args и **kwargs: переменное число аргументов
Базовый, но обязательный вопрос: как функция принимает произвольное число аргументов.
*argsсобирает лишние позиционные аргументы в кортеж,**kwargs— лишние именованные аргументы в словарь.
Вопрос: что такое *args и **kwargs?
Чёткий ответ. Это синтаксис для приёма любого числа аргументов. Звёздочки — это операторы упаковки: * упаковывает позиционные в tuple, ** упаковывает именованные в dict. Имена args/kwargs — конвенция, важны именно звёздочки.
def show(*args, **kwargs):
print("args:", args)
print("kwargs:", kwargs)
show(1, 2, 3, name="Аня", age=30)
Вывод:
args: (1, 2, 3)
kwargs: {'name': 'Аня', 'age': 30}
Распаковка: те же звёздочки в обратную сторону
При вызове функции * и ** работают наоборот — распаковывают коллекцию в отдельные аргументы.
def point(x, y, z):
return x + y + z
coords = [1, 2, 3]
print(point(*coords)) # распаковали список в три аргумента
params = {"x": 10, "y": 20, "z": 30}
print(point(**params)) # распаковали словарь в именованные
Вывод:
6 60
Практическое применение: обёртки и проксирование
Главная польза — функция-обёртка, которая принимает что угодно и передаёт дальше, не зная сигнатуры. Так пишут декораторы и адаптеры.
def with_log(func, *args, **kwargs):
print(f"вызываю {func.__name__}")
result = func(*args, **kwargs)
print(f"результат: {result}")
return result
def add(a, b):
return a + b
with_log(add, 2, 3)
with_log(add, a=10, b=20)
Вывод:
вызываю add результат: 5 вызываю add результат: 30
Порядок параметров: что за чем
На интервью любят спросить правильный порядок объявления параметров. Он строгий: сначала обычные позиционные, затем *args, затем именованные с дефолтами (их называют keyword-only, если они идут после *args), и в конце **kwargs. Нарушите порядок — будет синтаксическая ошибка.
def func(a, b, *args, sep="-", **kwargs):
print("a, b:", a, b)
print("args:", args)
print("sep:", sep)
print("kwargs:", kwargs)
func(1, 2, 3, 4, sep="/", x=10, y=20)
Вывод:
a, b: 1 2
args: (3, 4)
sep: /
kwargs: {'x': 10, 'y': 20}
Здесь a и b заняли первые два позиционных, лишние 3, 4 попали в args, sep взялся из именованного аргумента, а всё остальное именованное (x, y) — в kwargs. Важная деталь: параметр после *args можно передать только по имени — это удобно, чтобы заставить вызывающего писать sep="/" явно и не путать аргументы местами.
Итог
*args→ кортеж позиционных,**kwargs→ словарь именованных аргументов.- В вызове
*/**наоборот распаковывают коллекцию в аргументы. - Порядок параметров строгий: позиционные,
*args, keyword-only с дефолтами,**kwargs. - Главное применение — обёртки, которые передают аргументы дальше «как есть».