Прямой ответ
Звёздочка в сигнатуре функции собирает аргументы в коллекцию, а в вызове — разворачивает коллекцию в аргументы. Направление определяется местом использования.
def log(level, *args, **kwargs):
print(level, args, kwargs)
log("INFO", "сообщение", user=42)
# args = ('сообщение',), kwargs = {'user': 42}
params = {"user": 42, "timeout": 5}
connect(**params) # распаковка словаря
connect(*[1, 2], **params) # распаковка списка и словаря
| Место | Одна звёздочка | Две звёздочки |
|---|---|---|
| Сигнатура | Сбор позиционных в кортеж | Сбор именованных в словарь |
| Вызов | Развёртывание последовательности | Развёртывание словаря |
Сбор аргументов позволяет функции принимать произвольное число параметров, поэтому обёртка передаёт аргументы без явного перечисления. О применении в декораторах — в статье про декораторы.
Только позиционные, только именованные и порядок параметров
Сигнатура функции разделяет параметры на группы, поэтому порядок передачи определяется явно.
Только позиционные. Параметры до одиночной звёздочки передаются по позиции, поэтому передача по имени завершается ошибкой. Применяется, когда имя параметра не входит в контракт.
Только именованные. Параметры после одиночной звёздочки передаются по имени, поэтому позиция не учитывается. Применяется для параметров, которые не должны передаваться по позиции.
Произвольные аргументы. Сбор позиционных и именованных аргументов позволяет функции принимать параметры, не перечисленные в сигнатуре.
Практическое следствие: разделение параметров на группы фиксирует способ передачи, поэтому изменение способа завершается ошибкой, а не молчаливым изменением поведения.
Отдельный аспект — порядок параметров в сигнатуре. Группы следуют в определённом порядке, поэтому нарушение порядка является синтаксической ошибкой.
Три признака некорректного использования упаковки.
Передача именованного параметра по позиции для только именованного. Вызов завершается ошибкой, поскольку способ передачи зафиксирован.
Распаковка словаря с ключами, не соответствующими параметрам. Передача завершается ошибкой, поскольку ключ становится именем параметра.
Смешение явных аргументов и распаковки с конфликтом имён. Один параметр передаётся дважды, поэтому вызов завершается ошибкой.
О передаче аргументов в генераторы — в статье про генераторы и итераторы; о применении упаковки в метапрограммировании — в статье про метаклассы и дескрипторы.
Распаковка в присваивании и возврат нескольких значений
Механизм распаковки применяется не только к аргументам вызова, но и к присваиванию.
Присваивание с распаковкой. Последовательность разворачивается в переменные, поэтому возврат нескольких значений присваивается в одной строке. Число переменных должно соответствовать числу элементов.
Сбор остатка. Часть элементов собирается в список, поэтому остальные присваиваются явно. Применяется, когда число элементов заранее неизвестно.
Вложенная распаковка. Элемент последовательности сам является последовательностью, поэтому распаковывается на том же уровне.
Практическое следствие: распаковка в присваивании использует тот же механизм, что и в вызове функции, поэтому поведение единообразно.
Практика: использование упаковки и распаковки
1. Используйте сбор аргументов в обёртках. Функция передаёт аргументы без явного перечисления, поэтому применима к любой сигнатуре.
2. Фиксируйте только именованные параметры. Параметр, который не должен передаваться по позиции, размещается после одиночной звёздочки.
3. Фиксируйте только позиционные параметры. Параметр, имя которого не входит в контракт, размещается до разделителя.
4. Проверяйте соответствие ключей словаря параметрам. Ключ становится именем параметра, поэтому несоответствие завершается ошибкой.
5. Не смешивайте явный аргумент и распаровку с тем же именем. Параметр передаётся дважды, поэтому вызов завершается ошибкой.
6. Соблюдайте порядок групп параметров. Нарушение порядка является синтаксической ошибкой.
7. Документируйте принимаемые аргументы. Функция со сбором аргументов не показывает перечень в сигнатуре, поэтому описание требуется.
Рекомендации:
- Различайте сбор и развёртывание. Одна звёздочка в сигнатуре собирает, в вызове — разворачивает.
- Учитывайте тип собираемой коллекции. Позиционные аргументы собираются в кортеж, именованные — в словарь.
- Проверяйте поведение при пустой коллекции. Распаковка пустой последовательности не передаёт аргументов, поэтому параметры со значением по умолчанию используются.
- Фиксируйте способ передачи для параметров контракта. Только именованные параметры предотвращают передачу по позиции.
- Пересматривайте сигнатуру при изменении контракта. Изменение имени параметра влияет на вызовы с распаковкой словаря.
Практический сценарий: вызов с распаковкой словаря завершался ошибкой
Контекст: функция вызывалась с распаковкой словаря параметров, при этом часть вызовов завершалась ошибкой.
- Диагностика. Ключи словаря не соответствовали именам параметров функции после переименования одного параметра. Распаковка передавала ключ как имя параметра, поэтому несоответствие завершалось ошибкой.
- Причина. Переименование параметра не сопровождалось обновлением словарей, используемых для распаковки. Явная передача аргументов выявила бы ошибку при проверке кода, распаковка — только при выполнении.
- Действия. (а) Определили перечни ключей, используемые для распаковки, и привели их к именам параметров; (б) добавили проверку соответствия ключей в тестах; (в) зафиксировали имена параметров как контракт, поэтому изменение выполняется с уведомлением потребителей; (г) перевели часть вызовов на явную передачу аргументов, чтобы несоответствие выявлялось статически; (д) добавили описание принимаемых аргументов в документацию функции.
- Результат. Вызовы с распаковкой соответствуют именам параметров, а несоответствие выявляется тестами.
Вывод: распаковка словаря передаёт ключи как имена параметров, поэтому переименование параметра требует обновления словарей. Явная передача аргументов выявляет несоответствие статически.
FAQ
Что делает одна звёздочка в сигнатуре функции? Собирает позиционные аргументы в кортеж. Размещённые после неё параметры передаются только по имени.
Что делает две звёздочки в вызове? Разворачивает словарь, поэтому каждый ключ становится именем параметра. Несоответствие ключа имени параметра завершается ошибкой.
Можно ли смешивать явные аргументы и распаковку? Да, если имена не конфликтуют. Передача одного параметра дважды завершается ошибкой.
Зачем нужны только позиционные параметры? Имя параметра не входит в контракт, поэтому изменение имени не влияет на вызовы. Передача по имени завершается ошибкой.
Источники: Python tutorial arbitrary arguments, glossary argument, PEP 570 — ссылки в шапке.