July 12, 2018

Переменное число аргументов в Python. *args и **kwargs

Все знают, что у функций есть аргументы. Их может быть, скажем, 1 или 5, но что, если функция должна принимать сколько угодно аргументов? Допустим функция print: сколько аргументов ей ни передай, она всё равно все их обработает:

print(1,2,3)  # 1 2 3

Давайте разберёмся, как такое можно реализовать.

Массив аргументов: *args

При объявлении функции, можно после всех основных аргументов указать ещё один, начинающийся со звёздочки. Обычно его называют args(сокр. от arguments). Например объявим следующую функцию:

def test_args(a, b, *args):
  pass

Она принимает 2 обязательных аргумента: a и b и любое число дополнительных аргументов, которые будут переданы в args. Все вызовы ниже корректны:

test_args(1, 2)  # Указываем только a и b
test_args(1, 2, 3, 4, 5)  # a и b и 3 необязательных аргумента
test_args(1, 2, 5)  # 1 необязательный аргумент

В первом случае, args - пустой кортеж, во втором - (3,4,5), в третьем - (5). Значения идут в том порядке, в котором они были переданы.

Словарь аргументов: **kwargs

А что делать с именованными аргументами? По аналогии, после всех обязательных аргументов и args(если он есть) можно указать аргумент, который начинается с **, его принято называть kwargs(сокр. от keyword arguments). Это словарь всех значений, в котором ключ - имя аргумента, а ему соответствует значение аргумента. Рассмотрим следующую функцию:

def test_args_and_kwargs(a, *args, **kwargs):
	pass

Вызывать её можно так, например:

test_args_and_kwargs(1, 2, 3, first=1, second=2)

a будет равно 1

args равно (2, 3)

kwargs равно {"first":1, "second": 2}

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

Распаковка аргументов

Не все знают, что есть точно такие же обозначения и при вызове функций. Рассмотрим пример:

print(*(1,2,3,4))  # Выведет 1 2 3 4

Такое ощущение, что print просто передали 4 аргумента, да? Вы правы. Дело в том, что если к массиву или кортежу дописать * в начале, то питон "распакует" значения в соответствующие аргументы. Причём это можно писать в любом месте. Например так:

print(1, 2, *(3, 4, 5), 6)  # 1 2 3 4 5 6

Уже догадываетесь, что есть аналогия с kwargs? Чутьё вас не подводит:

print("HelloWorld", **{"end":"!!!"})  # Выведет HelloWorld!!!

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

Вместо заключения

Эти синтаксические конструкции дают много простора для творчества. Однако, по возможности, старайтесь передавать аргументы явно: это упрощает код для чтения, и к вам возникнет меньше вопросов.

Статья для группы SnakeBlog