Какова цель и использование **kwargs?
Описание проблемы
У меня есть несколько вопросов по использованию **kwargs
в Python.
- Я знаю, что можно использовать
**kwargs
при выполнении фильтрации объектов, например, с методомobjects.filter
, чтобы передавать аргументы. - Можно ли также использовать
**kwargs
для указания временных интервалов, например, вtimedelta(hours=time1)
? - Как именно это работает? Считается ли это "распаковкой" аргументов, как в примере
a, b = 1, 2
?
Буду благодарен за пояснения и примеры использования **kwargs
в различных контекстах.
5 ответ(ов)
kwargs
— это просто словарь, который добавляется к параметрам функции.
Словарь может содержать пары "ключ-значение", и именно это представляют собой kwargs
. Давайте разберёмся с этим подробнее.
Что касается вопроса "зачем это нужно", ответ не так прост.
Например, представьте гипотетическую ситуацию, где у вас есть интерфейс, который просто вызывает другие функции для выполнения задач:
def myDo(what, where, why):
if what == 'swim':
doSwim(where, why)
elif what == 'walk':
doWalk(where, why)
...
Теперь вы добавляете новый метод "drive":
elif what == 'drive':
doDrive(where, why, vehicle)
Но подождите! Теперь появляется новый параметр "vehicle", о котором вы не знали ранее. А это значит, что вам необходимо обновить сигнатуру функции myDo
.
Здесь на помощь приходят kwargs
— вы просто добавляете их в сигнатуру:
def myDo(what, where, why, **kwargs):
if what == 'drive':
doDrive(where, why, **kwargs)
elif what == 'swim':
doSwim(where, why, **kwargs)
Таким образом, вам не нужно будет изменять сигнатуру вашей интерфейсной функции каждый раз, когда изменяются какие-либо вызванные функции.
Это всего лишь один из примеров, когда kwargs
может быть полезен.
Судя по вашему запросу, вы хотите получить ответ в стиле Stack Overflow на русском языке. Вот перевод на русский язык ответа на ваш вопрос:
На основе того, что хороший пример иногда лучше долгого рассуждения, я напишу две функции, использующие все возможности передачи аргументов в Python (как позиционные, так и именованные). Вам будет легко понять, что они делают:
def f(a=0, *args, **kwargs):
print("Получено в f(a, *args, **kwargs)")
print("=> f(a=%s, args=%s, kwargs=%s" % (a, args, kwargs))
print("Вызов g(10, 11, 12, *args, d=13, e=14, **kwargs)")
g(10, 11, 12, *args, d=13, e=14, **kwargs)
def g(f, g=0, *args, **kwargs):
print("Получено в g(f, g=0, *args, **kwargs)")
print("=> g(f=%s, g=%s, args=%s, kwargs=%s)" % (f, g, args, kwargs))
print("Вызов f(1, 2, 3, 4, b=5, c=6)")
f(1, 2, 3, 4, b=5, c=6)
Вот вывод:
Вызов f(1, 2, 3, 4, b=5, c=6)
Получено в f(a, *args, **kwargs)
=> f(a=1, args=(2, 3, 4), kwargs={'c': 6, 'b': 5})
Вызов g(10, 11, 12, *args, d=13, e=14, **kwargs)
Получено в g(f, g=0, *args, **kwargs)
=> g(f=10, g=11, args=(12, 2, 3, 4), kwargs={'c': 6, 'b': 5, 'e': 14, 'd': 13})
Как видно из вывода, функция f
принимает один обязательный аргумент и может принимать неограниченное количество дополнительных позиционных и именованных аргументов. Это позволяет передать все параметры дальше в функцию g
, где они также могут быть использованы. Надеюсь, это пояснение помогло!
Мотив: *args
и **kwargs
служат заполнителями для аргументов, которые необходимо передать при вызове функции.
Используем *args
для вызова функции
def args_kwargs_test(arg1, arg2, arg3):
print("arg1:", arg1)
print("arg2:", arg2)
print("arg3:", arg3)
Теперь мы используем *args
, чтобы вызвать заранее определенную функцию:
# args могут быть либо "списком", либо "кортежем"
>>> args = ("two", 3, 5)
>>> args_kwargs_test(*args)
Результат:
arg1: two
arg2: 3
arg3: 5
Теперь используем **kwargs
, чтобы вызвать ту же функцию:
# аргумент с ключевым словом "kwargs" должен быть словарем
>>> kwargs = {"arg3": 3, "arg2": 'two', "arg1": 5}
>>> args_kwargs_test(**kwargs)
Результат:
arg1: 5
arg2: two
arg3: 3
Итог: *args
не имеет "умности", он просто передает переданные аргументы в параметры (в порядке слева направо), в то время как **kwargs
работает более разумно, размещая соответствующее значение в нужном месте.
kwargs
в **kwargs
- это всего лишь название переменной. Вы вполне можете использовать **anyVariableName
.
kwargs
расшифровывается как "именованные аргументы", но я считаю, что их лучше называть "именованные параметры", так как это просто параметры, передаваемые с указанием имен (я не вижу особого смысла в слове "ключевое" в термине "именованные аргументы". Обычно "ключевое" означает слова, зарезервированные языком программирования и не подлежащие использованию программистом для имен переменных. Но в случае с kwargs
такой ситуации не происходит). Мы задаем имена param1
и param2
для двух передаваемых значений, как в примере func(param1="val1", param2="val2")
, вместо того, чтобы передавать только значения: func(val1, val2)
. Поэтому я полагаю, что их следует правильно называть "произвольное число именованных аргументов", поскольку мы можем указать любое количество этих параметров (то есть аргументов), если функция имеет сигнатуру func(**kwargs)
.
Теперь, позвольте мне объяснить, что такое "именованные аргументы", а затем "произвольное число именованных аргументов" kwargs
.
Именованные аргументы
именованные аргументы должны следовать за позиционными аргументами
порядок именованных аргументов не важен
Пример:
def function1(param1, param2="arg2", param3="arg3"): print("\n" + str(param1) + " " + str(param2) + " " + str(param3) + "\n") function1(1) # 1 arg2 arg3 # 1 позиционный аргумент function1(param1=1) # 1 arg2 arg3 # 1 именованный аргумент function1(1, param2=2) # 1 2 arg3 # 1 позиционный и 1 именованный аргумент function1(param1=1, param2=2) # 1 2 arg3 # 2 именованных аргумента function1(param2=2, param1=1) # 1 2 arg3 # 2 именованных аргумента в другом порядке function1(1, param3=3, param2=2) # 1 2 3 # # function1() # недопустимо: отсутствует обязательный аргумент # function1(param2=2, 1) # недопустимо: SyntaxError: неназванный аргумент после именованного аргумента # function1(1, param1=11) # недопустимо: TypeError: function1() получил несколько значений для аргумента 'param1' # function1(param4=4) # недопустимо: TypeError: function1() получил неожиданный именованный аргумент 'param4'
Произвольное число именованных аргументов kwargs
Последовательность параметров функции:
- позиционные параметры
- формальный параметр для захвата произвольного числа аргументов (с префиксом
*
) - именованные формальные параметры
- формальный параметр для захвата произвольного числа именованных параметров (с префиксом
**
)
Пример:
def function2(param1, *tupleParams, param2, param3, **dictionaryParams): print("param1: " + param1) print("param2: " + param2) print("param3: " + param3) print("пользовательские кортежные параметры", "-" * 10) for p in tupleParams: print(str(p) + ",") print("пользовательские именованные параметры", "-" * 10) for k, v in dictionaryParams.items(): print(str(k) + ":" + str(v)) function2("arg1", "пользовательский параметр1", "пользовательский параметр2", "пользовательский параметр3", param3="arg3", param2="arg2", customNamedParam1="val1", customNamedParam2="val2" ) # Вывод: # # param1: arg1 # param2: arg2 # param3: arg3 # пользовательские кортежные параметры ---------- # пользовательский параметр1, # пользовательский параметр2, # пользовательский параметр3, # пользовательские именованные параметры ---------- # customNamedParam2:val2 # customNamedParam1:val1
Передача кортежа и словаря для пользовательских аргументов
Чтобы завершить, замечу, что мы можем передавать
- "формальный параметр для захвата произвольного числа аргументов" как переменную типа кортеж
- "формальный параметр для захвата произвольного числа именованных параметров" как переменную типа словарь
Таким образом, тот же самый вызов можно сделать следующим образом:
tupleCustomArgs = ("пользовательский параметр1", "пользовательский параметр2", "пользовательский параметр3")
dictCustomNamedArgs = {"customNamedParam1": "val1", "customNamedParam2": "val2"}
function2("arg1",
*tupleCustomArgs, # обратите внимание на *
param3="arg3",
param2="arg2",
**dictCustomNamedArgs # обратите внимание на **
)
Наконец, обратите внимание на *
и **
в вышеуказанных вызовах функций. Если мы опустим их, можем получить неожиданные результаты.
Опускание *
в аргументах кортежа:
function2("arg1",
tupleCustomArgs, # опускаем *
param3="arg3",
param2="arg2",
**dictCustomNamedArgs
)
выводит:
param1: arg1
param2: arg2
param3: arg3
пользовательские кортежные параметры ----------
('пользовательский параметр1', 'пользовательский параметр2', 'пользовательский параметр3'),
пользовательские именованные параметры ----------
customNamedParam2:val2
customNamedParam1:val1
Вышеприведенный кортеж ('пользовательский параметр1', 'пользовательский параметр2', 'пользовательский параметр3')
выводится без изменения.
Опускание **
в аргументах словаря:
function2("arg1",
*tupleCustomArgs,
param3="arg3",
param2="arg2",
dictCustomNamedArgs # опускаем **
)
доставляет:
dictCustomNamedArgs
^
SyntaxError: неназванный аргумент после именованного аргумента
В дополнение, вы также можете комбинировать различные способы использования при вызове функций с аргументами kwargs
:
def test(**kwargs):
print(kwargs['a'])
print(kwargs['b'])
print(kwargs['c'])
args = { 'b': 2, 'c': 3 }
test(a=1, **args)
Этот код выведет следующий результат:
1
2
3
Обратите внимание, что **kwargs
должен быть последним аргументом в списке параметров функции.
Как клонировать список, чтобы он не изменялся неожиданно после присваивания?
Сохранить график в файл изображения вместо его отображения
Преобразование списка словарей в DataFrame pandas
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе
Что такое Python egg?