Вступление
У некоторых функций нет аргументов, у других их несколько. Бывают случаи, когда у нас есть функции с аргументами, о которых мы не знаем заранее. У нас может быть переменное количество аргументов, потому что мы хотим предложить гибкий API другим разработчикам или мы не знаем размер ввода. С помощью Python мы можем создавать функции, принимающие любое количество аргументов.
В этой статье мы рассмотрим, как мы можем определять и использовать функции с аргументами переменной длины. Эти функции могут принимать неизвестное количество входных данных либо в виде последовательных записей, либо в виде именованных аргументов.
Использование множества аргументов с * args
Давайте реализуем функцию, которая находит минимальное значение между двумя числами. Это выглядело бы так:
def my_min(num1, num2):
if num1 < num2:
return num1
return num2
my_min(23, 50)
23
Он просто проверяет, меньше ли первое число второго. Если да, то возвращается первое число. В противном случае возвращается второе число.
Если мы хотим найти минимум 3 числа, мы можем добавить еще один аргумент
в my_min()
и другие операторы if. Если нашей минимальной функции нужно
найти наименьшее число из любой неопределенной суммы, мы можем
использовать список:
def my_min(nums):
result = nums[0]
for num in nums:
if num < result:
result = num
return result
my_min(4, 5, 6, 7, 2)
2
Хотя это работает, наши кодировщики теперь должны заключить свои номера в список, что не так просто, как это было, когда у нас было два или три определенных аргумента. Давайте возьмем лучшее из обоих миров с аргументами переменной длины.
Аргументы переменной длины , сокращенно varargs, - это аргументы, которые могут принимать неопределенное количество входных данных. Когда они используются, программисту не нужно заключать данные в список или альтернативную последовательность.
В Python varargs определяются с использованием синтаксиса *args
Давайте переопределим нашу my_min()
с помощью *args
:
def my_min(*args):
result = args[0]
for num in args:
if num < result:
result = num
return result
my_min(4, 5, 6, 7, 2)
2
Примечание :
args
- это просто имя, вы можете назвать этот vararg как угодно, если перед ним стоит одна звездочка (*
). Лучше всего называть егоargs
чтобы его можно было сразу узнать.
Любой аргумент, идущий после *args
должен быть именованным аргументом
- аргументом, на который ссылается его имя, а не его позиция. С помощью
*args
вы больше не можете ссылаться на другой аргумент по его позиции.
Кроме того, в функции можно использовать только *args
типа vararg.
Вы можете подумать, что решение с *args
очень похоже на решение со
списком. Это потому, что *args
внутренне является Tuple, который
представляет собой итеративную последовательность, аналогичную спискам.
Если вы хотите проверить его тип, вы можете ввести код в свой
интерпретатор Python:
$ python3
>>> def arg_type_test(*args):
... print(type(args))
...
>>> arg_type_test(1, 2)
<class 'tuple'>
С помощью *args
мы можем принимать несколько аргументов
последовательно, как это сделано в my_min()
. Эти аргументы
обрабатываются по их положению. Что, если бы мы хотели взять несколько
аргументов, но ссылаться на них по имени? Мы рассмотрим, как это сделать
в следующем разделе.
Использование множества именованных аргументов с ** kwargs
Python может принимать несколько аргументов ключевых слов , более
известных как **kwargs
. Он ведет себя аналогично *args
, но хранит
аргументы в словаре вместо кортежей:
def kwarg_type_test(**kwargs):
print(kwargs)
kwarg_type_test(a="hi")
kwarg_type_test(roses="red", violets="blue")
Результатом будет:
{'a': 'hi'}
{'roses': 'red', 'violets': 'blue'}
Используя словарь, **kwargs
может сохранить имена аргументов, но не
сможет сохранить их позицию.
Примечание . Как и
args
, вы можете использовать любое другое имя, кромеkwargs
. Однако передовая практика диктует, что вы должны постоянно использоватьkwargs
.
Поскольку **kwargs
- это словарь, вы можете перебирать их, как и любой
другой, с помощью .items()
:
def kwargs_iterate(**kwargs):
for i, k in kwargs.items():
print(i, '=', k)
kwargs_iterate(hello='world')
При запуске наша консоль покажет:
hello = world
Аргументы ключевых слов полезны, когда вы не уверены, будет ли аргумент доступен. Например, если бы у нас была функция для сохранения сообщения блога в базе данных, мы бы сохранили такую информацию, как контент и автора. Сообщение в блоге может иметь теги и категории, но они не всегда установлены.
Мы можем определить такую функцию:
def save_blog_post(content, author, tags=[], categories=[]):
pass
В качестве альтернативы мы позволяем вызывающей функции передавать любое
количество аргументов и связывать tags
и categories
случае, если они
установлены:
def save_blog_post(content, author, **kwargs):
if kwargs.get('tags'):
# Save tags with post
pass
if kwargs.get('categories'):
# Save categories with post
pass
Теперь, когда у нас есть понимание обоих типов поддержки аргументов переменной длины, давайте посмотрим, как мы можем объединить два в одной функции.
Комбинирование аргументов Varargs и ключевых слов
Довольно часто мы хотим использовать и *args
и **kwargs
вместе,
особенно при написании библиотек Python или повторно используемого кода.
К счастью для нас, *args
и **kwargs
прекрасно сочетаются друг с
другом, и мы можем использовать их следующим образом:
def combined_varargs(*args, **kwargs):
print(args)
print(kwargs)
combined_varargs(1, 2, 3, a="hi")
Если вы запустите этот фрагмент кода, вы увидите:
(1, 2, 3)
{'a': 'hi'}
При смешивании позиционных и именованных аргументов позиционные аргументы должны стоять перед именованными аргументами. Кроме того, аргументы фиксированной длины предшествуют аргументам переменной длины. Следовательно, получаем такой порядок:
- Известные позиционные аргументы
*args
- Известные именованные аргументы
**kwargs
Функция со всеми типами аргументов может выглядеть так:
def super_function(num1, num2, *args, callback=None, messages=[], **kwargs):
pass
Как только мы будем следовать этому порядку при определении и вызове функций с аргументами varargs и ключевыми словами, мы получим ожидаемое от них поведение.
До сих пор мы использовали *args
и **kwargs
для определений функций.
Python позволяет нам использовать тот же синтаксис при вызове функций.
Посмотрим как!
Распаковка аргументов с помощью * args и ** kwargs
Рассмотрим функцию add3()
, которая принимает 3 числа и выводит их
сумму. Мы можем создать это так:
def add3(num1, num2, num3):
print("The grand total is", num1 + num2 + num3)
Если у вас есть список чисел, вы можете использовать эту функцию, указав, какой элемент списка используется в качестве аргумента:
magic_nums = [32, 1, 7]
add3(magic_nums[0], magic_nums[1], magic_nums[2])
Если вы запустите этот код, вы увидите:
The grand total is 40
Хотя это работает, мы можем сделать его более лаконичным с помощью
синтаксиса *args
add3(*magic_nums)
Результат: The grand total is 40
, как и раньше.
Когда мы используем *args
в вызове функции, мы распаковываем
переменную. Под распаковкой мы подразумеваем, что мы извлекаем отдельные
значения из списка. В этом случае мы вытаскиваем каждый элемент списка и
помещаем их в аргументы, где позиция 0 соответствует первому аргументу.
Аналогичным образом можно распаковать кортеж:
tuple_nums = (32, 1, 7)
add3(*tuple_nums) # The grand total is 40
Если вы хотите распаковать словарь, вы должны использовать синтаксис
**kwargs
dict_nums = {
'num1': 32,
'num2': 1,
'num3': 7,
}
add3(**dict_nums) # The grand total is 40
В этом случае Python сопоставляет ключ словаря с именем аргумента и устанавливает его значение.
Вот и все! Вы можете упростить управление вызовами функций, распаковав значения вместо того, чтобы указывать каждый аргумент, которому требуется значение из объекта.
Заключение
В Python мы можем использовать *args
или **kwargs
для захвата
переменного числа аргументов в наших функциях. Используя *args
, мы
можем обрабатывать неопределенное количество аргументов в позиции
функции. С помощью **kwargs
мы можем получить неопределенное
количество аргументов по их имени.
Хотя функция может иметь только один аргумент переменной длины каждого типа, мы можем комбинировать оба типа функций в одном аргументе. В этом случае мы должны убедиться, что позиционные аргументы предшествуют именованным аргументам, а фиксированные аргументы - перед аргументами переменной длины.
Python также позволяет нам использовать синтаксис для вызовов функций.
Если у нас есть список или кортеж и используется *args
, он распакует
каждое значение как позиционные аргументы. Если у нас есть словарь и
используется **kwargs
, он будет сопоставлять имена ключей словаря с
именами аргументов функции.
Вы работаете над функцией, которая может извлечь выгоду из такого типа аргументов? Или, может быть, вы можете реорганизовать функцию и сделать ее пригодной для будущего? Сообщите нам, над чем вы работаете!