Python

Функции — жизнь, не так ли? Не важно, новичок вы в программировании в целом или пришли из другого языка: осваивая Python, вы узнаете, что число параметров, указанных в определении функции, совпадает с числом передаваемых аргументов. 

Это является основой и помогает понять устройство мира. Как бы то ни было, этот же принцип ставит вас в затруднительное положение, как только в определении функции вы видите *args или **kwargs.

Не позволяйте синтаксису вас напугать. Это никакие не особые параметры. Они вообще не особо вычурны, и мы сейчас научимся их правильно использовать.

Позиционные аргументы против именованных

Для понимания *args и **kwargs нам потребуется разобраться с двумя принципами, а точнее понять разницу между позиционными и именованными аргументами. В самых простых функциях мы играем в игру сопоставления — аргумент 1 сопровождает параметр 1, аргумент 2 сопровождает параметр 2 и т.д.

def printThese(a,b,c):
   print(a, "is stored in a")
   print(b, "is stored in b")
   print(c, "is stored in c")printThese(1,2,3)
"""
1 is stored in a
2 is stored in b
3 is stored in c
"""

Все три аргумента необходимы. Упущение любого из них вызовет ошибку.

def printThese(a,b,c):
   print(a, "is stored in a")
   print(b, "is stored in b")
   print(c, "is stored in c")printThese(1,2)
"""
TypeError: printThese() missing 1 required positional argument: 'c'
"""

Если мы назначим параметру в определении функции значение по умолчанию, тогда он станет опциональным.

def printThese(a,b,c=None):
   print(a, "is stored in a")
   print(b, "is stored in b")
   print(c, "is stored in c")printThese(1,2)
"""
1 is stored in a
2 is stored in b
None is stored in c
"""

Помимо этого, опциональные параметры получают возможность именования. То есть вы можете определить имя параметра в вызове функции, чтобы потом отобразить его.

Давайте назначим всем трем переменным значения по умолчанию none и посмотрим, как мы при этом сможем отобразить их независимо от последовательности. 

def printThese(a=None,b=None,c=None):
   print(a, "is stored in a")
   print(b, "is stored in b")
   print(c, "is stored in c")printThese(c=3, a=1)
"""
def printThese(a=None,b=None,c=None):
   print(a, "is stored in a")
   print(b, "is stored in b")
   print(c, "is stored in c")printThese(c=3, a=1)
"""
1 is stored in a
None is stored in b
3 is stored in c
"""

Оператор *

Позвольте мне начать с упоминания о том, как мне нравится этот оператор. Он такой… визуальный. Значок * больше всего ассоциируется с умножением, но в Python он делает то же самое, что spread (...) в JS.

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

a = [1,2,3]
b = [*a,4,5,6]print(b) # [1,2,3,4,5,6]

В этом примере мы берем содержимое a и распаковываем его в новый список b

Как использовать *args и **kwargs

Итак, нам известно, что оператор * распаковывает множественные значения и существует два типа параметров функции. Если вы до сих пор не догадались, то *args — это сокращение от arguments (аргументы), а **kwargs — это сокращение от keyword arguments (именованные аргументы).

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

Например, давайте создадим функцию для печати баллов для тестирования студента:

def printScores(student, *scores):
   print(f"Student Name: {student}")
   for score in scores:
      print(score)printScores("Jonathan",100, 95, 88, 92, 99)
"""
Student Name: Jonathan
100
95
88
92
99
"""

Так, подождите. Я не использовал имя *args? Все верно, “args” является стандартным обозначением, но при этом только лишь именем. Главная роль здесь кроется именно за единичной звездочкой, которая создает список, где содержимое является позиционными аргументами, которые изначально определяются вызовом функции. 

Теперь станет проще понять **kwargs. Имя здесь также не имеет значения, а вот двойная звездочка создает словарь, где содержимым являются именованные аргументы, также изначально определенные вызовом функции. Для наглядности создадим функцию печати имен животных, принадлежащих некоему человеку:

def printPetNames(owner, **pets):
   print(f"Owner Name: {owner}")
   for pet,name in pets.items():
      print(f"{pet}: {name}")printPetNames("Jonathan", dog="Brock", fish=["Larry", "Curly", "Moe"], turtle="Shelldon")"""
Owner Name: Jonathan
dog: Brock
fish: ['Larry', 'Curly', 'Moe']
turtle: Shelldon
"""

Заключение

Немного мудрых слов в напутствие вам для избежания распространенных ошибок и расширения знаний:

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

Читайте также:


Перевод статьи Jonathan Hsu: What Are *args and **kwargs in Python?