В Python достаточно часто интерфейсы функций частично или полностью «сворачивают» с помощью *args и **kwargs. Это вполне естественно для питонистов, но вызывает вопросы при переходе с других языков. С самим «сворачиванием» никаких проблем нет. Сложности возникают при модификации аргументов: строятся проверки вокруг args и kwargs, вместо того чтобы «развернуть» интерфейс функции.
Представьте себе такой кейс: есть две библиотеки, одна из которых имеет полностью прописанный интерфейс у функции foo. Вторая определяет функцию boo, которая вызывает foo. Интерфейс boo «свёрнут» с помощью *args и **kwargs:
def foo(arg1=None, arg2=None):
pass
def boo(*args, **kwargs):
foo(*args, **kwargs)
В коде проекта есть функция do_magic, которая использует boo:
def do_magic(*args, **kwargs):
boo(*args, **kwargs)
Задача такая: если arg1 в do_magic прилетает со значением по умолчанию, то дальше его нужно отправить со значением True.
Первый вариант решения: arg1 может быть либо первым элементом в args, либо находиться в kwargs. Значит, можно добавить в функцию соответствующую проверку:
def do_magic(*args, **kwargs):
if 'arg1' not in kwargs and len(args) == 0:
kwargs['arg1'] = True
boo(*args, **kwargs)
Работу мы выполнили: учли, что arg1 может быть передан как именованный параметр, так и как неименованный. Функция работает как надо. Но есть путь проще, элегантнее и с меньшим количеством кода:
def do_magic(arg1=True, *args, **kwargs):
boo(arg1=arg1, *args, **kwargs)
Почему второй вариант лучше?
- Простота и читаемость. Код становится короче и понятнее: сразу видно, какой параметр модифицируется и какое у него значение по умолчанию.
- Меньше проверок. Не нужно вручную проверять наличие параметра в
args/kwargs— Python сам обработает передачу аргументов. - Надёжность. Снижается риск ошибок из‑за сложной логики проверки позиций в
argsили дублирования ключей вkwargs. - Соответствие идиомам Python. Явное указание параметров делает интерфейс функции понятнее для других разработчиков и инструментов статического анализа.
- Удобство поддержки. Если потребуется изменить логику обработки других аргументов, код будет легче модифицировать.
Важные нюансы при использовании второго подхода:
- Если
arg1уже передан как позиционный аргумент (вargs), явное указаниеarg1=arg1приведёт к ошибке «multiple values for argument». Чтобы избежать этого, убедитесь, что логика вызова не допускает дублирования. - При наличии большого числа параметров такой подход может усложнить сигнатуру функции. В таких случаях стоит рассмотреть использование
**kwargsс предварительной обработкой или рефакторинг интерфейса. - Для сложных сценариев модификации аргументов можно комбинировать оба подхода: «развернуть» ключевые параметры, а остальные передать через
*args/**kwargs.