[PYTHON] Keyword argument routing

Summary

If you want to control the parameters passed from the caller to the library in the library wrapper function, you can use ** so that you do not have to add arguments each time even if the number of parameters you want to pass increases.

Keyword arguments

def func(k1,k2):
    print("%s".format(locals()))

When calling the function, the usual way of calling

>>> func("v1","v2")
{'k2': 'v2', 'k1': 'v1'}

Against

>>> func(k1="v1",k2="v2")
{'k2': 'v2', 'k1': 'v1'}

Arguments given in the form of key = value are called keyword arguments.

Even if you change the order when calling the function, the value will be passed where the key matches.

>>> func(k2="v2",k1="v1")
{'k2': 'v2', 'k1': 'v1'}

If the default value of the argument is not specified, an exception will be thrown if there are not enough arguments.

>>> func(k1="v1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 2 arguments (1 given)
>>> func(k2="v2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 2 arguments (1 given)

An error will occur if the keyword is duplicated or if a keyword that is not used in the argument name is specified.

>>> func(k1="v1-1",k1="v1-2")
  File "<stdin>", line 1
SyntaxError: keyword argument repeated
>>> func(k3="v3")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() got an unexpected keyword argument 'k3'

Give a dictionary

Instead of specifying key = value, give the dictionary with ** in front of it.

>>> func(**{"k1":"v1","k2":"v2"})
{'k2': 'v2', 'k1': 'v1'}
>>> func(**{"k2":"v2"})
{'k2': 'v2', 'k1': 'd1'}

Can be called as.

You can also mix the two writing styles:

>>> func(k1="v1",**{"k2":"v2"})
{'k2': 'v2', 'k1': 'v1'}

Receive a dictionary

If you write the last argument in the form of ** name in the function definition, you can receive a dictionary of keyword arguments.

def func(k0, **kwargs):
    print("{0}".format(locals()))

The call is

>>> func("v0")
{'k0': 'v0', 'kwargs': {}}
>>> func("v0",k1="v1",k2="v2")
{'k0': 'v0', 'kwargs': {'k2': 'v2', 'k1': 'v1'}}
>>> func(k0="v0",k1="v1",k2="v2")
{'k0': 'v0', 'kwargs': {'k2': 'v2', 'k1': 'v1'}}
>>> func(**{"k0":"v0","k1":"v1","k2":"v2"})
{'k0': 'v0', 'kwargs': {'k2': 'v2', 'k1': 'v1'}}

Can be done like this. However, if you do not add keywords

>>> func("v0","v1","v2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 1 argument (3 given)

Please note that it will cause an error like.

Parameter routing in the wrapper

For example, requests.get ()

get(url, params=None, **kwargs)

A function that wraps and returns the body

import requests

def get_body(url):
    r = requests.get(url)
    return r.text

if __name__ == '__main__':
    print(get_body('http://qiita.com'))

When you want to specify timeout, if there is

import requests

def get_body(url, timeout=None):
    r = requests.get(url, timeout=timeout)
    return r.text

if __name__ == '__main__':
    print(get_body('http://qiita.com', timeout=0.1))

However, if you want to specify headers or proxies in the future, you will need to increase the number of arguments. on the other hand,

import requests

def get_body(url, **kwargs):
    r = requests.get(url, **kwargs)
    return r.text

if __name__ == '__main__':
    print(get_body('http://qiita.com', timeout=0.1))

If you do like this, you don't need to do that (you have to look at the specifications of the library function you are calling to know the specifications of the arguments, so you need to make it easy to understand what you are wrapping. There is). By the way, the requests.get itself used in the example is also a wrapper for requests.request, and the parameters are routed in the same way as above.

References

Recommended Posts

Keyword argument routing
Variadic argument