*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.
  • Главное применение — обёртки, которые передают аргументы дальше «как есть».
Проверьте себя
1. Во что *args собирает аргументы?
AВ список
BВ кортеж
CВ словарь
DВ множество
2. Что делает point(*[1, 2, 3]) при def point(x, y, z)?
AПередаёт список как один аргумент
BРаспаковывает список в три аргумента x, y, z
CВызывает ошибку
DПередаёт кортеж
3. Во что **kwargs собирает именованные аргументы?
AВ кортеж
BВ список пар
CВ словарь
DВ строку
Поддержать проект