*args и **kwargs
Чему вы научитесь:
- писать функции, которые принимают любое число аргументов, через
*argsи**kwargs; - передавать функцию в аргументе и записывать короткую функцию через
lambda; - объяснять, как замыкание хранит переменные внешней функции и меняет их через
nonlocal; - писать декоратор, который добавляет поведение к любой функции.

Представьте воронку над коробкой. Сколько бы монет в неё ни упало — одна, три или ни одной, — все они окажутся в одной упаковке. Параметр со звёздочкой работает как такая воронка: он собирает все лишние аргументы в один кортеж.
Зачем это нужно? Функция print принимает сколько угодно аргументов: одно значение, три или ни одного.
У обычной функции число позиционных параметров задано в строке def. Лишний аргумент останавливает программу с ошибкой TypeError. Звёздочка перед параметром снимает это ограничение:
def total(*prices):
return sum(prices)
print(total(250, 100, 50)) # 400
print(total(7)) # 7
print(total()) # 0Давайте разберём пример по строкам:
def total(*prices)объявляет один параметр со звёздочкой — нашу воронку.total(250, 100, 50)кладёт три числа в кортеж(250, 100, 50), иsumвозвращает400.total(7)собирает кортеж из одного числа(7,), и сумма равна7.total()получает пустой кортеж(), а сумма пустого кортежа равна0.
Кортеж и словарь
Звёздочек в параметре может быть одна или две. От их числа зависит, какие аргументы попадут в параметр и во что Python их соберёт.
Одна звёздочка: кортеж
Параметр с одной звёздочкой собирает все лишние позиционные аргументы в кортеж. С ним работают как с любым кортежем: перебирают в for, передают в sum или len. Если лишних аргументов нет, кортеж пустой, а не None.
Две звёздочки: словарь
Параметр с двумя звёздочками собирает лишние именованные аргументы в словарь. Имя аргумента становится ключом, а его значение — значением. Без лишних именованных аргументов словарь тоже пустой.
Обе воронки можно поставить в одну функцию:
def show(first, *args, **kwargs):
print(first, args, kwargs)
show(1, 2, 3, color="red") # 1 (2, 3) {'color': 'red'}
show(1) # 1 () {}Параметры в строке def идут в таком порядке:
- обычные параметры;
*args;**kwargs— всегда последним.
Обычные параметры получают свои аргументы первыми, и в *args попадает только то, что осталось. В первом вызове 1 попадает в first, а args равен (2, 3). Во втором вызове лишнего нет, поэтому args и kwargs пустые.
Имена args и kwargs — соглашение, а не правило. Смысл задают только * и **, поэтому def total(*prices) работает так же.
Здесь аналогия с воронкой заканчивается: монеты в упаковке не перемешиваются. Кортеж хранит аргументы ровно в том порядке, в котором их передали.
Звёздочка в вызове
В вызове звёздочка работает наоборот: она не собирает, а раскладывает. Это та же распаковка, что и в уроке о кортежах:
*раскладывает список или кортеж на отдельные позиционные аргументы:print(*[3, 1, 2])равноprint(3, 1, 2)и печатает3 1 2;**раскладывает словарь на именованные аргументы:print(1, 2, **{"sep": "-"})равноprint(1, 2, sep="-")и печатает1-2.
Вместе они позволяют функции принять любые аргументы и передать их другой функции без изменений. Функция show_price ниже печатает приставку price:, а всё, что получила, передаёт в price:
def price(amount, currency="USD"):
return f"{amount} {currency}"
def show_price(*args, **kwargs):
print("price:", price(*args, **kwargs))
show_price(250) # price: 250 USD
show_price(250, currency="EUR") # price: 250 EURРазберём второй вызов по шагам:
show_price(250, currency="EUR")кладёт250вargs, аcurrency="EUR"— вkwargs.price(*args, **kwargs)раскладывает их обратно и становится вызовомprice(250, currency="EUR").priceвозвращает250 EUR, иshow_priceпечатает его после приставки.
Этот вопрос часто задают на собеседовании: «Что такое *args и **kwargs?» Хороший ответ состоит из трёх частей:
*argsсобирает лишние позиционные аргументы в кортеж, а**kwargs— лишние именованные в словарь;- если лишнего не передали, это пустые кортеж и словарь, а не
None; - их используют, чтобы функция принимала любое число аргументов или передавала их дальше.
Частая ошибка
Функции со звёздочкой передали готовый список:
def total(*prices):
return sum(prices)
print(total([250, 100])) # ✗TypeError: unsupported operand type(s) for +: 'int' and 'list'Список — один аргумент, поэтому prices равен ([250, 100],): кортежу из одного элемента-списка. Вы бросили в воронку целый кошелёк вместо монет, и в упаковке оказался один предмет. sum пытается прибавить этот список к числу 0 и останавливается. Распакуйте список в вызове: total(*[250, 100]) возвращает 350.
Главное
*argsсобирает лишние позиционные аргументы в кортеж,**kwargs— лишние именованные в словарь.- Без лишних аргументов они пустые —
()и{}, а неNone. - В вызове
*и**раскладывают список и словарь на отдельные аргументы. - Связка
*args, **kwargsпередаёт любые аргументы другой функции без изменений.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.