开发者

What is the purpose and use of **kwargs? [duplicate]

开发者 https://www.devze.com 2022-12-11 20:08 出处:网络
This question already has answers here: What does ** (double star/asterisk) and * (star/asterisk) do for parameters?
This question already has answers here: What does ** (double star/asterisk) and * (star/asterisk) do for parameters? 开发者_如何学JAVA (25 answers) Closed last year.

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 and args, 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 names param1 and param2 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) if func has signature func(**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:
    1. positional parameters
    2. formal parameter capturing arbitrary number of arguments (prefixed with *)
    3. named formal parameters
    4. 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号