Аргументы переменной длины в Python с * args и ** kwargs

Введение У некоторых функций нет аргументов, у других их несколько. Бывают случаи, когда у нас есть функции с аргументами, о которых мы не знаем заранее. У нас может быть переменное количество аргументов, потому что мы хотим предложить гибкий API другим разработчикам или мы не знаем размер ввода. С помощью Python мы можем создавать функции, принимающие любое количество аргументов. В этой статье мы рассмотрим, как мы можем определять и использовать функции с аргументами переменной длины. Эти функции могут принимать неизвестные значения am

Вступление

У некоторых функций нет аргументов, у других их несколько. Бывают случаи, когда у нас есть функции с аргументами, о которых мы не знаем заранее. У нас может быть переменное количество аргументов, потому что мы хотим предложить гибкий 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'} 

При смешивании позиционных и именованных аргументов позиционные аргументы должны стоять перед именованными аргументами. Кроме того, аргументы фиксированной длины предшествуют аргументам переменной длины. Следовательно, получаем такой порядок:

  1. Известные позиционные аргументы
  2. *args
  3. Известные именованные аргументы
  4. **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 , он будет сопоставлять имена ключей словаря с именами аргументов функции.

Вы работаете над функцией, которая может извлечь выгоду из такого типа аргументов? Или, может быть, вы можете реорганизовать функцию и сделать ее пригодной для будущего? Сообщите нам, над чем вы работаете!

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus