Функции с переменным числом аргументов; args, kwargs

В Python можно написать функцию, принимающую произвольное количество аргументов.

Допустим, мы хоти написать функцию, которая выводит среднее арифметическое всех своих аргументов. Мы можем, используя предыдущие знания, написать эту функцию, например, для трёх аргументов:

def mean(elem1, elem2, elem3):
    return (elem1 + elem2 + elem3) / 3

Или для четырёх. Или для двух. Но только для какого-то конкретного известного заранее количества аргументов

Можно сказать, что мы будем принимать только 1 аргумент - список со значениями; и пусть вызывающий засовывает все переменные в массив перед вызовом функции.

Распаковка позиционных аргументов, args

Однако в Python есть способ передавать произвольное количество аргументов. Делается это через оператор распаковки:

def mean(*args):
    return sum(args) / len(args)

Что же происходит в строке def mean(*args)? Аргументы распаковываются в переменную args.

После распаковки переменная args будет содержать кортеж со значениями аргументов.

Можно распаковывать не все аргументы, а лишь последние переданные. Например, код ниже распечатает среднее арифметическое всех аргументов, кроме первого:

a = 10
b = c = d = 1
def mean(first, *args):
    return sum(args) / len(args)

print(mean(a, b, c, d))

Распаковка аргументов напоминает распаковку кортежа, где кортеж - это аргументы, которые мы передадим при вызове. Сравните:

a = 10
b = c = d = 1

def mean(first, *args):
    return sum(args) / len(args)

print(mean(a, b, c, d))

и

a = 10
b = c = d = 1

def mean(first, args):
    return sum(args) / len(args)

a, *args = (a, b, c, d)
print(mean(a, args))

Распаковка именованных аргументов, kwargs

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

И распаковываться аргументы будут в словарь, где ключами служат имена аргументов, а значениями - значения аргументов:

def print_kwargs(**kwargs):
    return kwargs

print(print_kwargs(a=1, b=2, c=3, d=4))
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}

Например, можно вернуть среднее арифметическое всех переменных, кроме a:

def mean(**kwargs):
    kwargs.pop("a", 0)
    return sum(kwargs.values()) / len(kwargs)

print(mean(a=1, b=2, c=3, d=4))

args и kwargs можно комбинировать. Однако, как и в обычных функциях, сначала идут позиционные, а потом именованные аргументы. Например, среднее арифметическое всех аргументов:

def mean(*args, **kwargs):
    total = sum(args) + sum(kwargs.values())
    length = len(args) + len(kwargs)
    return total / length

print(mean(1, 1, new=4))

Почему переменные после распаковки называются args и kwargs соответственно? args - сокращение от arguments, kwargs - сокращение от keyword arguments.

В Python так исторически сложилось, что подобные переменные называют именно так. Ничто не мешает называть их по-другому, но остальным проще будет читать код, если при написании Вы не будете отходить от общепринятых соглашений.