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

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

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