Как работает синтаксис функции с аргументом после звездочки в Python и зачем он нужен
Что проверяют
Знаете ли вы, что «после звёздочки» это две разные конструкции: сбор произвольных позиционных аргументов в *args и голая *, которая делает все последующие параметры доступными только по имени.
Как отвечать
*Первое значение, `args`.** Собирает все лишние позиционные аргументы в кортеж. Полезно, когда количество заранее неизвестно, и в обёртках, которые пробрасывают вызов дальше.
def total(*args):
return sum(args) # args это кортеж
total(1, 2, 3) # 6*Второе значение, голая ``.** Ничего не собирает, а работает разделителем: параметры после неё становятся keyword-only, передать их позиционно нельзя.
def connect(host, *, timeout=5, retries=3):
...
connect("db", timeout=10) # ок
connect("db", 10) # TypeErrorЗачем это нужно:
- Читаемость вызова.
create(user, True, False)не расшифровывается без похода в сигнатуру,create(user, notify=True, dry_run=False)понятен на месте. - Совместимость. Keyword-only параметр можно добавить в середину списка, не сломав существующие вызовы, потому что позиции они не занимают.
- Защита от перепутанных флагов одного типа: поменять местами два булевых аргумента позиционно легко, по имени невозможно.
*После `args тоже идут keyword-only параметры**: def join(*items, sep=" "). Именно так устроен print, у которого sep, end, file и flush` передаются только по имени.
Симметричная конструкция / (Python 3.8, PEP 570) отмечает параметры, которые можно передать только позиционно. Полная форма сигнатуры:
def f(pos_only, /, normal, *, kw_only):
...`kwargs`** собирает именованные аргументы в словарь и в сигнатуре всегда идёт последним.
Не путать объявление и вызов. В сигнатуре звёздочка собирает, в точке вызова наоборот распаковывает: f(*seq, **mapping) разворачивает последовательность в позиционные аргументы, а словарь в именованные. Типовой декоратор строится ровно на этой паре:
def wrapper(*args, **kwargs):
return target(*args, **kwargs)Где спотыкаются
Сводят весь вопрос к *args и не знают про голую звёздочку, хотя вопрос сформулирован именно про аргумент после неё. Вторая ошибка, считать, что звёздочка в вызове тоже «собирает», хотя там она выполняет обратную операцию.