*args и **kwargs в Python
В программировании на Python мы часто работаем с функциями, которые создаем, чтобы сделать код более пригодным для повторного использования и более понятным.
Если нам нужно выполнить аналогичную операцию несколько раз, вместо копирования и вставки одного и того же кода мы создаем функцию, которую мы вызываем несколько раз с новыми аргументами для выполнения этой операции.
*args и **kwargs позволяют нам передавать в функцию переменное количество аргументов, тем самым расширяя возможности повторного использования созданных нами функций.
Обратите внимание, что использование *args и **kwargs в основном зависит от того, какие операции будут выполнять ваши функции и насколько они гибки, чтобы принимать больше аргументов в качестве входных данных.
*args в Python
Давайте начнем с понимания того, что такое *args и как его можно использовать!
*args используется для передачи переменного количества аргументов без ключевых слов с использованием следующего синтаксиса:
def my_func(*args): #do something
Вы можете использовать *args , если не уверены, сколько аргументов функция будет получать при каждом использовании.
Например, предположим, что у вас есть функция, которая складывает два числа и возвращает их сумму:
#Define a function def add(x, y): #Add two numbers result = x+y #Print the result print(f'Result: ')
и если бы вы протестировали эту функцию с примерными значениями:
#Test with sample values add(3, 5)
вы должны получить:
Пока функция add() работает правильно, так как принимает только 2 аргумента и возвращает результат.
А что, если вы хотите сложить 3 числа вместе?
Вы можете попробовать использовать функцию add() :
#Test with 3 values add(3, 5, 9)
но вы получите TypeError:
TypeError: add() takes 2 positional arguments but 3 were given
Это происходит потому, что когда мы определяли функцию add() , мы специально определили ее с двумя аргументами x и y, поэтому при передаче третьего аргумента она должна возвращать ошибку.
Но как сделать эту функцию более надежной, чтобы она принимала на вход больше аргументов?
Вот где *args становится удобным!
Давайте перепишем функцию add() , чтобы она принимала *args в качестве аргумента:
#Define a function def add(*args): #Initialize result at 0 result = 0 #Iterate over args tuple for arg in args: result += arg #Print the result print(f'Result: ')
*args позволит нам передать переменное количество аргументов, не являющихся ключевыми словами, в функцию add() . Он будет передан как tuple, что означает, что мы можем перебирать его, используя цикл for .
Теперь давайте проверим эту функцию с примерными значениями:
#Test with 3 arguments add(3, 5, 9) #Test with 5 arguments add(3, 5, 9, 11, 15)
и вы должны получить:
Result: 17 Result: 43
Теперь мы сделали функцию add() более пригодной для повторного использования, используя *args , чтобы заставить ее работать с переменным числом аргументов, не являющихся ключевыми словами.
**kwargs в Python
Давайте начнем с понимания того, что такое *args и как его можно использовать!
**kwargs используется для передачи переменного количества аргументов с ключевыми словами, используя следующий синтаксис:
def my_func(**kwargs): #do something
Вы можете использовать **kwargs , если не уверены, сколько аргументов будет получать функция при каждом использовании.
Обратите внимание, что основное различие между *args и **kwargs заключается в том, что *args допускает переменное количество аргументов без ключевых слов, тогда как **kwargs допускает переменное количество аргументов с ключевыми словами.
Например, предположим, что у вас есть функция, которая принимает два аргумента ключевого слова и печатает их значения:
#Define function def print_vals(name, age): #Print first argument print(f'name: ') #Print second argument print(f'age: ')
и если бы вы протестировали эту функцию с примерными значениями:
#Test with sample values print_vals(name='Mike', age=20)
вы должны получить:
name: Mike age: 20
Пока что функция print_vals() работает правильно, так как принимает только 2 аргумента и печатает их.
А что, если вы хотите напечатать 3 значения?
Вы можете попробовать использовать функцию print_vals() :
#Test with 3 values print_vals(name='Mike', age=20, city='New York')
но вы получите TypeError:
TypeError: print_vals() got an unexpected keyword argument 'city'
Это происходит потому, что когда мы определяли функцию print_vals() , мы специально определили ее с двумя аргументами ключевого слова name и age , поэтому при передаче третьего аргумента ключевого слова она должна возвращать ошибку.
Но как сделать эту функцию более надежной, чтобы она принимала на вход больше аргументов с ключевыми словами?
Вот где **kwargs становится удобным!
Давайте перепишем функцию add() , чтобы она принимала *args в качестве аргумента:
#Define function def print_vals(**kwargs): #Iterate over kwargs dictionary for key, value in kwargs.items(): #Print key-value pairs print(f': ')
**kwargs позволит нам передавать переменное количество аргументов ключевого слова в функцию print_vals() . Он будет передан как словарь, что означает, что мы можем перебирать его с помощью цикла for .
Теперь давайте проверим эту функцию с примерными значениями:
#Test with 3 keyword arguments print_vals(name='Mike', age=20, city='New York') #Test with 5 keyword arguments print_vals(name='Mike', age=20, city='New York', height=6.0, weight=180)
и вы должны получить:
name: Mike age: 20 city: New York name: Mike age: 20 city: New York height: 6.0 weight: 180
Теперь мы сделали функцию print_vals() более пригодной для повторного использования, используя **kwargs , чтобы заставить ее работать с переменным количеством аргументов ключевого слова.
Выводы
В этой статье мы рассмотрели *args и **kwargs в Python и их использование в функциях с примерами.
Теперь, когда вы знаете основные функции, вы можете попрактиковаться, переписав часть существующего кода в разных проектах и попробовав решить более сложные варианты использования.
*args и **kwargs
Вы можете использовать звездочку * при написании функции, чтобы собрать все позиционные (т.е. безымянные) аргументы в кортеже:
def print_args(farg, *args): print("formal arg: %s" % farg) for arg in args: print("another positional arg: %s" % arg)
print_args(1, "two", 3)
В этом вызове farg будет назначен как всегда, а два других будут переданы в кортеж args в порядке их получения.
Использование ** kwargs при написании функций
Вы можете определить функцию , которая принимает произвольное количество ключевых слов (называемые) аргументов, используя двойную звезду ** перед именем параметра:
def print_kwargs(**kwargs): print(kwargs)
При вызове метода Python создаст словарь всех аргументов ключевого слова и сделает его доступным в теле функции:
print_kwargs(a="two", b=3) # prints: ""
Обратите внимание, что параметр ** kwargs в определении функции всегда должен быть последним параметром, и он будет соответствовать только аргументам, переданным после предыдущих.
def example(a, **kw): print kw example(a=2, b=3, c=4) # =>
Внутри тела функции, kwargs манипулируют таким же образом , как словарь; для того , чтобы получить доступ к отдельным элементам в kwargs вы просто цикл через них , как вы бы с нормальным словарем:
def print_kwargs(**kwargs): for key in kwargs: print("key = , value = ".format(key, kwargs[key]))
Теперь, вызывая print_kwargs(a=»two», b=1) показывает следующий вывод:
print_kwargs(a = "two", b = 1) key = a, value = "two" key = b, value = 1
Использование ** kwargs при вызове функций
Вы можете использовать словарь для присвоения значений параметрам функции; используя имя параметра в качестве ключей в словаре и значение этих аргументов, привязанных к каждому ключу:
def test_func(arg1, arg2, arg3): # Usual function with three arguments print("arg1: %s" % arg1) print("arg2: %s" % arg2) print("arg3: %s" % arg3) # Note that dictionaries are unordered, so we can switch arg2 and arg3. Only the names matter. kwargs = # Bind the first argument (ie. arg1) to 1, and use the kwargs dictionary to bind the others test_var_args_call(1, **kwargs)
Использование * args при вызове функций
Эффект использования * оператора на аргумент при вызове функции является то , что распаковывать списка или кортежа аргумент
def print_args(arg1, arg2): print(str(arg1) + str(arg2)) a = [1,2] b = tuple([3,4]) print_args(*a) # 12 print_args(*b) # 34
Обратите внимание, что длина помеченного аргумента должна быть равна числу аргументов функции.
Обычный Python идиомы является использование оператора распаковки * с zip функции ликвидировать ее последствия:
a = [1,3,5,7,9] b = [2,4,6,8,10] zipped = zip(a,b) # [(1,2), (3,4), (5,6), (7,8), (9,10)] zip(*zipped) # (1,3,5,7,9), (2,4,6,8,10)
Только ключевые слова и ключевые слова обязательные аргументы
Python 3 позволяет вам определять аргументы функции, которые могут быть назначены только по ключевому слову, даже без значений по умолчанию. Это делается с помощью звезды , чтобы потреблять дополнительные позиционные параметры без установки параметров ключевых слов. Все аргументы после того , как это ключевое слово-только (т.е. не позиционные) аргументы. Обратите внимание, что если для аргументов только для ключевых слов не задано значение по умолчанию, они все равно необходимы при вызове функции.
def print_args(arg1, *args, keyword_required, keyword_only=True): print("first positional arg: <>".format(arg1)) for arg in args: print("another positional arg: <>".format(arg)) print("keyword_required value: <>".format(keyword_required)) print("keyword_only value: <>".format(keyword_only)) print(1, 2, 3, 4) # TypeError: print_args() missing 1 required keyword-only argument: 'keyword_required' print(1, 2, 3, keyword_required=4) # first positional arg: 1 # another positional arg: 2 # another positional arg: 3 # keyword_required value: 4 # keyword_only value: True
Заполнение значений kwarg словарем
def foobar(foo=None, bar=None): return "<><>".format(foo, bar) values = foobar(**values) # "foobar"
** kwargs и значения по умолчанию
Использовать значения по умолчанию с ** kwargs
def fun(**kwargs): print kwargs.get('value', 0) fun() # print 0 fun(value=1) # print 1
Больше об именованных аргументах — Python: Функции
В этом уроке мы разберем, как получать произвольное количество именованных аргументов, как передавать их в виде коллекции и как объявлять keyword-only аргументы.
Получение именованных аргументов в виде словаря
Позиционные аргументы можно получать в виде *args , причем в произвольном количестве. Для именованных аргументов тоже существует подобная возможность. Только они получаются в виде словаря, что позволяет сохранить имена аргументов в ключах:
def g(**kwargs): return kwargs g(x=1, y=2, z=None) #
По соглашению аргумент, который получает подобный словарь, принято называть kwargs — от словосочетания keyword arguments.
Порядок вызовов смешанных аргументов
Поэкспериментируем с разными комбинациями аргументов, которые можно передавать функциям:
def f(*args, **kwargs): return (args, kwargs) f(1, 2, 3, 4, kx='a', ky='b', kz='c') # ((1, 2, 3, 4), )
Заметим, что *args всегда указывается перед **kwargs , иначе будет ошибка:
def f(**kwargs, *args): return (kwargs, args) f(1, kx='a') # SyntaxError: invalid syntax
Также при объявлении функций можно комбинировать позиционные аргументы, значения по умолчанию, *args и **kwagrs одновременно. При использовании обычных позиционных аргументов их следует добавлять в начало перед аргументом *args :
def f(x, *args, **kwargs): return (x, args, kwargs) f(1, 2, 3, kx='a', ky='b') # (1, (2, 3), )
Аргументы со значением по умолчанию следует добавлять после аргумента *args , но перед аргументом **kwagrs :
def f(*args, ky=42, **kwargs): return (args, ky, kwargs) f(1, 2, 3, 4, kz='c') # ((1, 2, 3, 4), 42, ) f(1, 2, 3, 4, ky='b', kz='c') # ((1, 2, 3, 4), 'b', )
Аргумент *args в определении функции пишется после всех обычных позиционных аргументов, но перед первым аргументом со значением по умолчанию. А **kwargs пишется в самом конце после последнего аргумента со значением по умолчанию.
Согласно этому правилу у нас идет следующий порядок расстановки аргументов:
- Обычные позиционные аргументы
- Аргумент *args
- Аргументы со значением по умолчанию
- Аргумент **kwagrs
def f(x, y, *args, kx=None, ky=42, **kwargs): return (x, y, args, kx, ky, kwargs) f(1, 2, 3, 4, kx='a', ky='b', kz='c') # (1, 2, (3, 4), 'a', 'b', )
В реальном коде редко какая функция использует все эти возможности одновременно. Но понимать, как работает каждая форма объявления аргументов, и как такие формы можно сочетать, — очень важно.
Передача именованных аргументов с помощью словаря
Как и в случае позиционных аргументов, именованные можно передавать в функцию пачкой в виде словаря. Для этого перед словарем нужно поставить две звездочки. Пример:
def coords(x, y): return (x, y) coords(x=1, **'y': 2>) # (1, 2)
Здесь указан обычный именованный аргумент, а другой завернут в словарь.
Попробуем вызвать функцию с двумя наборами аргументов: для позиционных и для именованных:
def f(x, y, *args, kx=None, ky=42, **kwargs): return (x, y, args, kx, ky, kwargs) positional = (2, 3) named = dict(ky='b', kz='c') f(1, *positional, 4, kx='a', **named) # (1, 2, (3, 4), 'a', 'b', )
В этом примере мы не написали литерал, а вместо этого вызвали функцию dict с несколькими именованными аргументами. Так словарь еще больше похож на сохраненный набор аргументов.
При подстановке аргументов разворачивающиеся наборы аргументов вроде *positional и **named можно указывать вперемешку с аргументами соответствующего типа: *positional — с позиционными, **named — с именованными. При этом все именованные аргументы должны идти после всех позиционных.
Keyword-only аргументы
В Python 3 добавили возможность пометить именованные аргументы функции так, чтобы вызывать функцию можно было только через передачу этих аргументов по именам. Такие аргументы называются keyword-only и их нельзя передать в функцию в виде позиционных. Выглядит функция с подобными аргументами так:
def open_file(name, *, writable=False, binary=False): … f1 = open_file('foo.txt', writable=True) f2 = open_file('bar.bin', binary=True) f3 = open_file('raw.dat', True, True) # TypeError: open_file() takes 1 positional argument but 3 were given
Здесь * выступает разделителем — отделяет обычные аргументы от строго именованных. Такой разделитель можно использовать только один раз в одном определении. Еще его нельзя применять в функциях с *args . Но можно объявлять функции, у которых будут только строго именованные аргументы. Для этого нужно поставить звездочку в самом начале перечня аргументов.
Этот пример демонстрирует подход к описанию аргументов. Первый аргумент — имя файла, который будет открыт. Оно всегда присутствует и связано по смыслу с именем функции. Поэтому этот аргумент можно не именовать. А writable и binary — необязательные аргументы, которые получают значения True / False . Поэтому опции и объявлены так, что могут быть указаны только явно.
Когда мы используем keyword-only аргументы вместе с именованными аргументами ( **kwargs ), возникает проблема. Именованные аргументы могут перехватить значения, которые должны были быть переданы как keyword-only аргументы. В итоге это может привести к ошибкам в работе функции.
Рассмотрим следующий пример:
def my_func(a, *, b=None, **kwargs): print(a, b, kwargs) my_func(1, b=2, c=3) # 1 2
В данном примере аргумент a принимает значение 1 , а keyword-only аргумент b — значение 2 . При этом аргумент c передается в **kwargs , что может привести к ошибке, если c не должен был передаваться этой функции.
Поэтому рекомендуется использовать либо keyword-only аргументы, либо **kwargs , но не оба вместе. Это поможет избежать неожиданного поведения и ошибок в работе функции.
Порядок аргументов при вызове функций
При вызове функций у нас больше свободы, чтобы задать порядок аргументов. Одиночные именованные аргументы могут идти вперемешку с подстановками наборов позиционных. Вот пример такого вызова:
def f(x, y, *args, kx=None, ky=42, **kwargs): return (x, y, args, kx, ky, kwargs) foo = [1, 2, 3] bar = "abc" f(kx=42, *foo, ky=100, *bar) # (1, 2, (3, 'a', 'b', 'c'), 42, 100, <>)
Еще одна особенность заключается в том, что мы не можем одновременно указать аргумент x по имени и при этом развернуть набор параметров для функции с сигнатурой вида f(x, *args) . То есть мы не сможем сделать так: f(*foo, x=42) .
Выводы
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Что такое *args и **kwargs в 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: ")
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: ")
for pet,name in pets.items():
print(f": ")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 , но по недосмотру оказывается именованным параметром.
- Вы можете использовать оператор * в том числе и в вызовах функций.
- Полное руководство по встроенным структурам данных Python
- Обработка естественного языка в Python. Основы
- Создаем чат-бот в Python с помощью nltk