[Перевод] Что такое *args и **kwargs в Python?
Функции — это жизнь. Правда? Если вы только начали осваивать Python, неважно — первый ли это ваш язык программирования, или вы пришли в Python из другого языка, то вы уже знаете о том, что количество параметров в объявлении функции соответствует количеству аргументов, которые передают функции при вызове.
Это — основы. Это то, что помогает людям понимать окружающий мир. Но утверждение «количество параметров равно количеству аргументов» закладывает в голову новичка бомбу замедленного действия, которая срабатывает после того, как он увидит в объявлении функции таинственные конструкции *args
или **kwargs
.
Не позволяйте всяким значкам загонять себя в ступор. Тут нет ничего архисложного. В общем-то, если эти конструкции вам незнакомы — предлагаю с ними разобраться.
Позиционные и именованные аргументы
Для того чтобы разобраться с *args
и **kwargs
, нам нужно освоить концепции позиционных (positional) и именованных (keyword) аргументов.
Сначала поговорим о том, чем они отличаются. В простейшей функции мы просто сопоставляем позиции аргументов и параметров. Аргумент №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)
"""
1 is stored in a
None is stored in b
3 is stored in c
"""
Оператор «звёздочка»
Оператор *
чаще всего ассоциируется у людей с операцией умножения, но в Python он имеет и другой смысл.
Этот оператор позволяет «распаковывать» объекты, внутри которых хранятся некие элементы. Вот пример:
a = [1,2,3]
b = [*a,4,5,6]
print(b) # [1,2,3,4,5,6]
Тут берётся содержимое списка a
, распаковывается, и помещается в список b
.
Как пользоваться *args и **kwargs
Итак, мы знаем о том, что оператор «звёздочка» в Python способен «вытаскивать» из объектов составляющие их элементы. Знаемы мы и о том, что существует два вида параметров функций. Вполне возможно, что вы уже додумались до этого сами, но я, на всякий случай, скажу об этом. А именно, *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
. Вместо неё у меня — *scores
. Нет ли тут ошибки? Ошибки здесь нет. Дело в том, что «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
для захвата позиционных и именованных аргументов. - Конструкцию
**kwarg
s нельзя располагать до*args
. Если это сделать — будет выдано сообщение об ошибке. - Остерегайтесь конфликтов между именованными параметрами и
**kwargs
, в случаях, когда значение планируется передать как**kwarg
-аргумент, но имя ключа этого значения совпадает с именем именованного параметра. - Оператор
*
можно использовать не только в объявлениях функций, но и при их вызове.
Уважаемые читатели! Какими параметрами вы чаще всего пользуетесь при объявлении Python-функций?