Автор оригинала: Sajjad Heydari.
Вступление
Некоторые функции не имеют аргументов, другие имеют несколько. Бывают случаи, когда у нас есть функции с аргументами, о которых мы заранее не знаем. У нас может быть переменное количество аргументов, потому что мы хотим предложить гибкий API другим разработчикам или мы не знаем размер входных данных. С помощью Python мы можем создавать функции, которые принимают любое количество аргументов.
В этой статье мы рассмотрим, как мы можем определять и использовать функции с аргументами переменной длины. Эти функции могут принимать неизвестное количество входных данных либо в виде последовательных записей, либо в виде именованных аргументов.
Использование многих аргументов с помощью *args
Давайте реализуем функцию, которая находит минимальное значение между двумя числами. Это будет выглядеть так:
def my_min(num1, num2): if num1 < num2: return num1 return num2 my_min(23, 50)
23
Он просто проверяет, меньше ли первое число, чем второе. Если это так, то возвращается первое число. В противном случае возвращается второе число.
Если мы хотим найти минимум 3 числа, мы можем добавить еще один аргумент в my_mind()
и другие 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
вы больше не можете ссылаться на другой аргумент по его позиции.
Кроме того, вы можете иметь только on *args
тип vararg в функции.
Возможно, вы думаете, что решение с *args
очень похоже на решение list. Это потому, что *args
внутренне является кортежем, который представляет собой итеративную последовательность, подобную спискам. Если вы хотите проверить его тип, вы можете ввести код в свой интерпретатор Python:
$ python3 >>> def arg_type_test(*args): ... print(type(args)) ... >>> arg_type_test(1, 2)
С помощью *args
мы можем принимать несколько аргументов последовательно , как это делается в my_mind()
. Эти аргументы обрабатываются их позицией. Что делать, если мы хотим взять несколько аргументов, но ссылаться на них по имени? Мы рассмотрим, как это сделать, в следующем разделе.
Использование многих именованных аргументов с помощью **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
В качестве альтернативы мы разрешаем вызывающей функции передавать любое количество аргументов и связывать только теги
и категории
, если они заданы:
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
- Известные именованные аргументы
**кварги
Функция со всеми типами аргументов может выглядеть следующим образом:
def super_function(num1, num2, *args, callback=None, messages=[], **kwargs): pass
Как только мы будем следовать этому порядку при определении и вызове функций с аргументами varargs и keyword, мы получим поведение, которое мы ожидаем от них.
До сих пор мы использовали синтаксис *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)
Выход – Общая сумма-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
, то он будет сопоставлять имена ключей словаря с именами аргументов функции.
Работаете ли вы над функцией, которая может извлечь выгоду из таких аргументов? Или, может быть, вы можете рефакторировать функцию и сделать ее будущей проверкой? Дайте нам знать, над чем вы работаете!