What is the purpose and use of **kwargs? [duplicate]
What are the uses for **kwargs
in Python?
I know you can do an objects.filter
on a table and pass in a **kwargs
argument.
Can I also do this for specifying time deltas i.e. timedelta(hours = time1)
?
How exactly does it work? Is it classified as 'unpacking'? Like a,b=1,2
?
You can use **kwargs
to let your functions take an arbitrary number of keyword arguments ("kwargs" means "keyword arguments"):
>>> 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
You can also use the **kwargs
syntax when calling functions by constructing a dictionary of keyword arguments and passing it to your function:
>>> kwargs = {'first_name': 'Bobby', 'last_name': 'Smith'}
>>> print_keyword_args(**kwargs)
first_name = Bobby
last_name = Smith
The Python Tutorial contains a good explanation of how it works, along with some nice examples.
Python 3 update
For Python 3, instead of iteritems()
, use items()
Unpacking dictionaries
**
unpacks dictionaries.
This
func(a=1, b=2, c=3)
is the same as
args = {'a': 1, 'b': 2, 'c':3}
func(**args)
It's useful if you have to construct parameters:
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)
Packing parameters of a function
- Use
.items()
instead of.iteritems()
for python 3
def setstyle(**styles):
for key, value in styles.iteritems(): # styles is a regular dictionary
setattr(someobject, key, value)
This lets you use the function like this:
setstyle(color="red", bold=False)
Notes
kwargs
is variable name used for keyword arguments, another variable name can be used. The important part is that it's a dictionary and it's unpacked with the double asterisk operator**
.- Other iterables are unpacked with the single asterisk operator
*
- To prevent confusion, it's probably best to stick with the recognized variable names,
kwargs
andargs
, for dictionaries and other iterables respectively.
Resources
- PEP 448: Additional Unpacking Generalizations
- Real Python: Python args and kwargs: Demystified
- What do * and ** before a variable name mean in a function signature?
kwargs is just a dictionary that is added to the parameters.
A dictionary can contain key, value pairs. And that are the kwargs. Ok, this is how.
The what for is not so simple.
For example (very hypothetical) you have an interface that just calls other routines to do the job:
def myDo(what, where, why):
if what == 'swim':
doSwim(where, why)
elif what == 'walk':
doWalk(where, why)
...
Now you get a new method "drive":
elif what == 'drive':
doDrive(where, why, vehicle)
But wait a minute, there is a new parameter "vehicle" -- you did not know it before. Now you must add it to the signature of the myDo-function.
Here you can throw kwargs into play -- you just add kwargs to the signature:
def myDo(what, where, why, **kwargs):
if what == 'drive':
doDrive(where, why, **kwargs)
elif what == 'swim':
doSwim(where, why, **kwargs)
This way you don't need to change the signature of your interface function every time some of your called routines might change.
This is just one nice example you could find kwargs helpful.
On the basis that a good sample is sometimes better than a long discourse I will write two functions using all python variable argument passing facilities (both positional and named arguments). You should easily be able to see what it does by yourself:
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)
And here is the output:
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
and **kwargs
serves as a placeholder for the arguments that need to be passed to a function call
using *args
and **kwargs
to call a function
def args_kwargs_test(arg1, arg2, arg3):
print "arg1:", arg1
print "arg2:", arg2
print "arg3:", arg3
Now we'll use *args
to call the above defined function
#args can either be a "list" or "tuple"
>>> args = ("two", 3, 5)
>>> args_kwargs_test(*args)
result:
arg1: two
arg2: 3
arg3: 5
Now, using **kwargs
to call the same function
#keyword argument "kwargs" has to be a dictionary
>>> kwargs = {"arg3":3, "arg2":'two', "arg1":5}
>>> args_kwargs_test(**kwargs)
result:
arg1: 5
arg2: two
arg3: 3
Bottomline : *args
has no intelligence, it simply interpolates the passed args to the parameters(in left-to-right order) while **kwargs
behaves intelligently by placing the appropriate value @ the required place
kwargs
in**kwargs
is just variable name. You can very well have**anyVariableName
kwargs
stands for "keyword arguments". But I feel they should better be called as "named arguments", as these are simply arguments passed along with names (I dont find any significance to the word "keyword" in the term "keyword arguments". I guess "keyword" usually means words reserved by programming language and hence not to be used by the programmer for variable names. No such thing is happening here in case of kwargs.). So we give namesparam1
andparam2
to two parameter values passed to the function as follows:func(param1="val1",param2="val2")
, instead of passing only values:func(val1,val2)
. Thus, I feel they should be appropriately called "arbitrary number of named arguments" as we can specify any number of these parameters (that is, arguments) iffunc
has signaturefunc(**kwargs)
So being said that let me explain "named arguments" first and then "arbitrary number of named arguments" kwargs
.
Named arguments
- named args should follow positional args
- order of named args is not important
Example
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'
Arbitrary number of named arguments kwargs
- Sequence of function parameters:
- positional parameters
- formal parameter capturing arbitrary number of arguments (prefixed with *)
- named formal parameters
- formal parameter capturing arbitrary number of named parameters (prefixed with **)
Example
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
Passing tuple and dict variables for custom args
To finish it up, let me also note that we can pass
- "formal parameter capturing arbitrary number of arguments" as tuple variable and
- "formal parameter capturing arbitrary number of named parameters" as dict variable
Thus the same above call can be made as follows:
tupleCustomArgs = ("custom param1", "custom param2", "custom param3")
dictCustomNamedArgs = {"customNamedParam1":"val1", "customNamedParam2":"val2"}
function2("arg1",
*tupleCustomArgs, #note *
param3="arg3",
param2="arg2",
**dictCustomNamedArgs #note **
)
Finally note *
and **
in function calls above. If we omit them, we may get ill results.
Omitting *
in tuple args:
function2("arg1",
tupleCustomArgs, #omitting *
param3="arg3",
param2="arg2",
**dictCustomNamedArgs
)
prints
param1: arg1
param2: arg2
param3: arg3
custom tuple params ----------
('custom param1', 'custom param2', 'custom param3'),
custom named params ----------
customNamedParam2:val2
customNamedParam1:val1
Above tuple ('custom param1', 'custom param2', 'custom param3')
is printed as is.
Omitting dict
args:
function2("arg1",
*tupleCustomArgs,
param3="arg3",
param2="arg2",
dictCustomNamedArgs #omitting **
)
gives
dictCustomNamedArgs
^
SyntaxError: non-keyword arg after keyword arg
As an addition, you can also mix different ways of usage when calling kwargs functions:
def test(**kwargs):
print kwargs['a']
print kwargs['b']
print kwargs['c']
args = { 'b': 2, 'c': 3}
test( a=1, **args )
gives this output:
1
2
3
Note that **kwargs has to be the last argument
Here's a simple function that serves to explain the usage:
def print_wrap(arg1, *args, **kwargs):
print(arg1)
print(args)
print(kwargs)
print(arg1, *args, **kwargs)
Any arguments that are not specified in the function definition will be put in the args
list, or the kwargs
list, depending on whether they are keyword arguments or not:
>>> print_wrap('one', 'two', 'three', end='blah', sep='--')
one
('two', 'three')
{'end': 'blah', 'sep': '--'}
one--two--threeblah
If you add a keyword argument that never gets passed to a function, an error will be raised:
>>> print_wrap('blah', dead_arg='anything')
TypeError: 'dead_arg' is an invalid keyword argument for this function
kwargs are a syntactic sugar to pass name arguments as dictionaries(for func), or dictionaries as named arguments(to func)
Here is an example that I hope is helpful:
#! /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] )
When you run the program, you get:
$ 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
The key take away here is that the variable number of named arguments in the call translate into a dictionary in the function.
Keyword Arguments are often shortened to kwargs in Python. In computer programming,
keyword arguments refer to a computer language's support for function calls that clearly state the name of each parameter within the function call.
The usage of the two asterisk before the parameter name, **kwargs, is when one doesn't know how many keyword arguments will be passed into the function. When that's the case, it's called Arbitrary / Wildcard Keyword Arguments.
One example of this is Django's receiver functions.
def my_callback(sender, **kwargs):
print("Request finished!")
Notice that the function takes a sender argument, along with wildcard keyword arguments (**kwargs); all signal handlers must take these arguments. All signals send keyword arguments, and may change those keyword arguments at any time. In the case of request_finished, it’s documented as sending no arguments, which means we might be tempted to write our signal handling as my_callback(sender).
This would be wrong – in fact, Django will throw an error if you do so. That’s because at any point arguments could get added to the signal and your receiver must be able to handle those new arguments.
Note that it doesn't have to be called kwargs, but it needs to have ** (the name kwargs is a convention).
This is the simple example to understand about python unpacking,
>>> def f(*args, **kwargs):
... print 'args', args, 'kwargs', kwargs
eg1:
>>>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
In Java, you use constructors to overload classes and allow for multiple input parameters. In python, you can use kwargs to provide similar behavior.
java example: https://beginnersbook.com/2013/05/constructor-overloading/
python example:
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.
just another way to think about it.
精彩评论