What is the purpose and use of **kwargs?
Каковы виды использования для **kwargs
в питоне?
Я знаю, что вы можете сделать objects.filter
на стол и пройти в **kwargs
аргумент.
Могу ли я также сделать это для указания дельты времени, т.е. timedelta(hours = time1)
?
Как именно это работает? Это классы как "распаковка"? подобно a,b=1,2
?
10 ответов
Ты можешь использовать **kwargs
чтобы позволить вашим функциям принимать произвольное количество аргументов ключевого слова ("kwargs" означает "аргументы ключевого слова"):
>>> def print_keyword_args(**kwargs):
... # kwargs is a dict of the keyword args passed to the function
... for key, value in kwargs.iteritems():
... print "%s = %s" % (key, value)
...
>>> print_keyword_args(first_name="John", last_name="Doe")
first_name = John
last_name = Doe
Вы также можете использовать **kwargs
синтаксис при вызове функций путем создания словаря аргументов ключевых слов и передачи его в вашу функцию:
>>> kwargs = {'first_name': 'Bobby', 'last_name': 'Smith'}
>>> print_keyword_args(**kwargs)
first_name = Bobby
last_name = Smith
Python Tutorial содержит хорошее объяснение того, как он работает, а также несколько хороших примеров.
<- Update ->
Для людей, использующих Python 3, вместо iteritems() используйте items ()
Распаковка словарей
**
распаковывает словари.
это
func(a=1, b=2, c=3)
такой же как
args = {'a': 1, 'b': 2, 'c':3}
func(**args)
Это полезно, если вам нужно построить параметры:
args = {'name': person.name}
if hasattr(person, "address"):
args["address"] = person.address
func(**args) # either expanded to func(name=person.name) or
# func(name=person.name, address=person.address)
Параметры упаковки функции
def setstyle(**styles):
for key, value in styles.iteritems(): # styles is a regular dictionary
setattr(someobject, key, value)
Это позволяет вам использовать функцию следующим образом:
setstyle(color="red", bold=False)
kwargs - это просто словарь, который добавляется к параметрам.
Словарь может содержать пары ключ-значение. И это карги. Хорошо, это как.
Причем не все так просто.
Например (очень гипотетически) у вас есть интерфейс, который просто вызывает другие процедуры для выполнения работы:
def myDo(what, where, why):
if what == 'swim':
doSwim(where, why)
elif what == 'walk':
doWalk(where, why)
...
Теперь вы получаете новый метод "езды":
elif what == 'drive':
doDrive(where, why, vehicle)
Но подождите минуту, появился новый параметр "транспортное средство" - вы не знали его раньше. Теперь вы должны добавить его в подпись myDo-функции.
Здесь вы можете бросить kwargs в игру - вы просто добавляете kwargs в подпись:
def myDo(what, where, why, **kwargs):
if what == 'drive':
doDrive(where, why, **kwargs)
elif what == 'swim':
doSwim(where, why, **kwargs)
Таким образом, вам не нужно менять сигнатуру вашей интерфейсной функции каждый раз, когда некоторые из вызываемых вами подпрограмм могут измениться.
Это только один хороший пример, который вы могли бы найти полезными для kwargs.
Исходя из того, что хороший пример иногда лучше длинного дискурса, я напишу две функции, использующие все средства передачи аргументов переменных Python (как позиционные, так и именованные аргументы). Вы должны легко увидеть, что он делает самостоятельно:
def f(a = 0, *args, **kwargs):
print("Received by f(a, *args, **kwargs)")
print("=> f(a=%s, args=%s, kwargs=%s" % (a, args, kwargs))
print("Calling 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("Received by g(f, g = 0, *args, **kwargs)")
print("=> g(f=%s, g=%s, args=%s, kwargs=%s)" % (f, g, args, kwargs))
print("Calling f(1, 2, 3, 4, b = 5, c = 6)")
f(1, 2, 3, 4, b = 5, c = 6)
И вот вывод:
Calling f(1, 2, 3, 4, b = 5, c = 6)
Received by f(a, *args, **kwargs)
=> f(a=1, args=(2, 3, 4), kwargs={'c': 6, 'b': 5}
Calling g(10, 11, 12, *args, d = 13, e = 14, **kwargs)
Received by 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})
Motif: *args
а также **kwargs
служит заполнителем для аргументов, которые необходимо передать в вызов функции
с помощью *args
а также **kwargs
вызвать функцию
def args_kwargs_test(arg1, arg2, arg3):
print "arg1:", arg1
print "arg2:", arg2
print "arg3:", arg3
Теперь мы будем использовать *args
вызвать определенную выше функцию
#args can either be a "list" or "tuple"
>>> args = ("two", 3, 5)
>>> args_kwargs_test(*args)
результат:
arg1: два
arg2: 3
arg3: 5
Теперь, используя **kwargs
вызвать ту же функцию
#keyword argument "kwargs" has to be a dictionary
>>> kwargs = {"arg3":3, "arg2":'two', "arg1":5}
>>> args_kwargs_test(**kwargs)
результат:
arg1: 5
arg2: два
arg3: 3
Нижняя линия: *args
не имеет интеллекта, он просто интерполирует переданные аргументы к параметрам (в порядке слева направо), в то время как **kwargs
ведет себя разумно, поместив соответствующее значение в требуемом месте
kwargs
в**kwargs
это просто имя переменной. Вы можете очень хорошо иметь**anyVariableName
kwargs
расшифровывается как "ключевые аргументы". Но я считаю, что их лучше называть "именованными аргументами", так как это просто аргументы, передаваемые вместе с именами (я не нахожу никакого значения для слова "ключевое слово" в термине "ключевые слова аргументы". Я думаю, что "ключевое слово" обычно означает слова, зарезервированные языком программирования и, следовательно, не используемые программистом для имен переменных. В случае с kwargs такого не происходит.). Итак, мы даем именаparam1
а такжеparam2
чтобы два значения параметров передавались в функцию следующим образом:func(param1="val1",param2="val2")
вместо передачи только значений:func(val1,val2)
, Таким образом, я считаю, что их следует называть "произвольным числом именованных аргументов", так как мы можем указать любое количество этих параметров (то есть аргументов), еслиfunc
имеет подпись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 positional arg function1(param1=1) #1 arg2 arg3 #1 named arg function1(1,param2=2) #1 2 arg3 #1 positional arg, 1 named arg function1(param1=1,param2=2) #1 2 arg3 #2 named args function1(param2=2, param1=1) #1 2 arg3 #2 named args out of order function1(1, param3=3, param2=2) #1 2 3 # #function1() #invalid: required argument missing #function1(param2=2,1) #invalid: SyntaxError: non-keyword arg after keyword arg #function1(1,param1=11) #invalid: TypeError: function1() got multiple values for argument 'param1' #function1(param4=4) #invalid: TypeError: function1() got an unexpected keyword argument 'param4'
Произвольное количество именованных аргументовkwargs
- Последовательность параметров функции:
- позиционные параметры
- формальный параметр, фиксирующий произвольное количество аргументов (с префиксом *)
- именованные формальные параметры
- формальный параметр захватывает произвольное количество именованных параметров (с префиксом **)
пример
def function2(param1, *tupleParams, param2, param3, **dictionaryParams): print("param1: "+ param1) print("param2: "+ param2) print("param3: "+ param3) print("custom tuple params","-"*10) for p in tupleParams: print(str(p) + ",") print("custom named params","-"*10) for k,v in dictionaryParams.items(): print(str(k)+":"+str(v)) function2("arg1", "custom param1", "custom param2", "custom param3", param3="arg3", param2="arg2", customNamedParam1 = "val1", customNamedParam2 = "val2" ) # Output # #param1: arg1 #param2: arg2 #param3: arg3 #custom tuple params ---------- #custom param1, #custom param2, #custom param3, #custom named params ---------- #customNamedParam2:val2 #customNamedParam1:val1
Передача переменных кортежа и dict для пользовательских аргументов
Чтобы закончить это, позвольте мне также отметить, что мы можем пройти
- "формальный параметр, фиксирующий произвольное количество аргументов" в качестве переменной кортежа и
- "формальный параметр, фиксирующий произвольное количество именованных параметров" в качестве переменной dict
Таким образом, тот же вышеупомянутый вызов может быть сделан следующим образом:
tupleCustomArgs = ("custom param1", "custom param2", "custom param3")
dictCustomNamedArgs = {"customNamedParam1":"val1", "customNamedParam2":"val2"}
function2("arg1",
*tupleCustomArgs, #note *
param3="arg3",
param2="arg2",
**dictCustomNamedArgs #note **
)
Наконец обратите внимание *
а также **
в вызовах функций выше. Если мы их опускаем, мы можем получить плохие результаты.
Опуская *
в кортежах:
function2("arg1",
tupleCustomArgs, #omitting *
param3="arg3",
param2="arg2",
**dictCustomNamedArgs
)
печать
param1: arg1
param2: arg2
param3: arg3
custom tuple params ----------
('custom param1', 'custom param2', 'custom param3'),
custom named params ----------
customNamedParam2:val2
customNamedParam1:val1
Выше кортежа ('custom param1', 'custom param2', 'custom param3')
печатается как есть.
Опуская dict
арг:
function2("arg1",
*tupleCustomArgs,
param3="arg3",
param2="arg2",
dictCustomNamedArgs #omitting **
)
дает
dictCustomNamedArgs
^
SyntaxError: non-keyword arg after keyword arg
Кроме того, вы также можете смешивать разные способы использования при вызове функций 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 должен быть последним аргументом
Вот простая функция, которая служит для объяснения использования:
def print_wrap(arg1, *args, **kwargs):
print(arg1)
print(args)
print(kwargs)
print(arg1, *args, **kwargs)
Все аргументы, которые не указаны в определении функции, будут помещены в args
список или kwargs
список, в зависимости от того, являются ли они ключевыми аргументами или нет:
>>> print_wrap('one', 'two', 'three', end='blah', sep='--')
one
('two', 'three')
{'end': 'blah', 'sep': '--'}
one--two--threeblah
Если вы добавите ключевой аргумент, который никогда не будет передан функции, появится ошибка:
>>> print_wrap('blah', dead_arg='anything')
TypeError: 'dead_arg' is an invalid keyword argument for this function
kwargs - это синтаксический сахар для передачи именованных аргументов в виде словарей (для func) или словарей в качестве именованных аргументов (для func)
Вот пример, который, я надеюсь, будет полезен:
#! /usr/bin/env python
#
def g( **kwargs) :
print ( "In g ready to print kwargs" )
print kwargs
print ( "in g, calling f")
f ( **kwargs )
print ( "In g, after returning from f")
def f( **kwargs ) :
print ( "in f, printing kwargs")
print ( kwargs )
print ( "In f, after printing kwargs")
g( a="red", b=5, c="Nassau")
g( q="purple", w="W", c="Charlie", d=[4, 3, 6] )
Когда вы запускаете программу, вы получаете:
$ python kwargs_demo.py
In g ready to print kwargs
{'a': 'red', 'c': 'Nassau', 'b': 5}
in g, calling f
in f, printing kwargs
{'a': 'red', 'c': 'Nassau', 'b': 5}
In f, after printing kwargs
In g, after returning from f
In g ready to print kwargs
{'q': 'purple', 'c': 'Charlie', 'd': [4, 3, 6], 'w': 'W'}
in g, calling f
in f, printing kwargs
{'q': 'purple', 'c': 'Charlie', 'd': [4, 3, 6], 'w': 'W'}
In f, after printing kwargs
In g, after returning from f
Ключевым моментом здесь является то, что переменное число именованных аргументов в вызове переводится в словарь в функции.
Аргументы ключевых слов в Python часто сокращаются до kwargs. В компьютерном программировании,
аргументы ключевого слова относятся к поддержке компьютерным языком для вызовов функций, которые четко указывают имя каждого параметра в вызове функции.
Две звездочки перед именем параметра **kwargs используются тогда, когда неизвестно, сколько аргументов ключевого слова будет передано в функцию. В этом случае это называется аргументами произвольного / подстановочного ключевого слова.
Одним из примеров этого являются функции приемника Django.
def my_callback(sender, **kwargs):
print("Request finished!")
Обратите внимание, что функция принимает аргумент отправителя вместе с аргументами ключевого слова с подстановочными знаками (**kwargs); все обработчики сигналов должны принимать эти аргументы. Все сигналы отправляют аргументы ключевого слова и могут изменить эти аргументы ключевого слова в любое время. В случае request_finished он задокументирован как отправка без аргументов, что означает, что у нас может возникнуть соблазн записать нашу обработку сигнала как my_callback(sender).
Это было бы неправильно - на самом деле, Django выдаст ошибку, если вы это сделаете. Это потому, что в любой момент к сигналу могут быть добавлены аргументы, и ваш получатель должен уметь обрабатывать эти новые аргументы.
Обратите внимание, что он не обязательно должен называться kwargs, но он должен иметь ** (имя kwargs является условным).
Это простой пример для понимания распаковки Python,
>>> def f(*args, **kwargs):
... print 'args', args, 'kwargs', kwargs
например, 1:
>>>f(1, 2)
>>> args (1,2) kwargs {} #args return parameter without reference as a tuple
>>>f(a = 1, b = 2)
>>> args () kwargs {'a': 1, 'b': 2} #args is empty tuple and kwargs return parameter with reference as a dictionary
В Java вы используете конструкторы для перегрузки классов и разрешения нескольких входных параметров. В python вы можете использовать kwargs для обеспечения аналогичного поведения.
Пример java: https://beginnersbook.com/2013/05/constructor-overloading/
пример Python:
class Robot():
# name is an arg and color is a kwarg
def __init__(self,name, color='red'):
self.name = name
self.color = color
red_robot = Robot('Bob')
blue_robot = Robot('Bob', color='blue')
print("I am a {color} robot named {name}.".format(color=red_robot.color, name=red_robot.name))
print("I am a {color} robot named {name}.".format(color=blue_robot.color, name=blue_robot.name))
>>> I am a red robot named Bob.
>>> I am a blue robot named Bob.
просто еще один способ подумать об этом.