Перейти к содержимому
Теория · Функции как значения · урок 1 из 10

*args и **kwargs

Чему вы научитесь

Чему вы научитесь:

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

Представьте воронку над коробкой. Сколько бы монет в неё ни упало — одна, три или ни одной, — все они окажутся в одной упаковке. Параметр со звёздочкой работает как такая воронка: он собирает все лишние аргументы в один кортеж.

Зачем это нужно? Функция print принимает сколько угодно аргументов: одно значение, три или ни одного.

У обычной функции число позиционных параметров задано в строке def. Лишний аргумент останавливает программу с ошибкой TypeError. Звёздочка перед параметром снимает это ограничение:

Python
def total(*prices):
    return sum(prices)


print(total(250, 100, 50))  # 400
print(total(7))  # 7
print(total())  # 0

Давайте разберём пример по строкам:

  1. def total(*prices) объявляет один параметр со звёздочкой — нашу воронку.
  2. total(250, 100, 50) кладёт три числа в кортеж (250, 100, 50), и sum возвращает 400.
  3. total(7) собирает кортеж из одного числа (7,), и сумма равна 7.
  4. total() получает пустой кортеж (), а сумма пустого кортежа равна 0.

Кортеж и словарь

Звёздочек в параметре может быть одна или две. От их числа зависит, какие аргументы попадут в параметр и во что Python их соберёт.

Одна звёздочка: кортеж

Параметр с одной звёздочкой собирает все лишние позиционные аргументы в кортеж. С ним работают как с любым кортежем: перебирают в for, передают в sum или len. Если лишних аргументов нет, кортеж пустой, а не None.

Две звёздочки: словарь

Параметр с двумя звёздочками собирает лишние именованные аргументы в словарь. Имя аргумента становится ключом, а его значение — значением. Без лишних именованных аргументов словарь тоже пустой.

Обе воронки можно поставить в одну функцию:

Python
def show(first, *args, **kwargs):
    print(first, args, kwargs)


show(1, 2, 3, color="red")  # 1 (2, 3) {'color': 'red'}
show(1)  # 1 () {}

Параметры в строке def идут в таком порядке:

  1. обычные параметры;
  2. *args;
  3. **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:

Python
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

Разберём второй вызов по шагам:

  1. show_price(250, currency="EUR") кладёт 250 в args, а currency="EUR" — в kwargs.
  2. price(*args, **kwargs) раскладывает их обратно и становится вызовом price(250, currency="EUR").
  3. price возвращает 250 EUR, и show_price печатает его после приставки.

Этот вопрос часто задают на собеседовании: «Что такое *args и **kwargs?» Хороший ответ состоит из трёх частей:

  • *args собирает лишние позиционные аргументы в кортеж, а **kwargs — лишние именованные в словарь;
  • если лишнего не передали, это пустые кортеж и словарь, а не None;
  • их используют, чтобы функция принимала любое число аргументов или передавала их дальше.

Частая ошибка

Функции со звёздочкой передали готовый список:

Python
def total(*prices):
    return sum(prices)


print(total([250, 100]))  # ✗
TEXT
TypeError: unsupported operand type(s) for +: 'int' and 'list'

Список — один аргумент, поэтому prices равен ([250, 100],): кортежу из одного элемента-списка. Вы бросили в воронку целый кошелёк вместо монет, и в упаковке оказался один предмет. sum пытается прибавить этот список к числу 0 и останавливается. Распакуйте список в вызове: total(*[250, 100]) возвращает 350.

Главное

  • *args собирает лишние позиционные аргументы в кортеж, **kwargs — лишние именованные в словарь.
  • Без лишних аргументов они пустые — () и {}, а не None.
  • В вызове * и ** раскладывают список и словарь на отдельные аргументы.
  • Связка *args, **kwargs передаёт любые аргументы другой функции без изменений.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом