8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

キーワード引数の引き回し

Posted at

まとめ

ライブラリのラッパー関数において、呼び出し元からライブラリに渡すパラメータを制御したい場合に、** を活用すると、渡したいパラメータが増えてきてもいちいち引数を追加して回らなくて済むようにできる。

キーワード引数

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

という関数を呼ぶとき、普通の呼び方

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

に対して、

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

のように key=value の形で与える引数をキーワード引数と呼ぶ。

関数呼び出し時の順序を入れ替えてもkeyが合うところに値を渡してくれる。

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

引数のデフォルト値が指定されていない場合、引数が足りないと例外が発生する。

>>> 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)

キーワードが重複した場合とか、引数名で使われていないキーワードを指定した場合はエラーになる。

>>> 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'

辞書を与える

key=value と指定する代わりに、辞書の手前に ** を付けたものを与えて

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

のように呼ぶことができる。

二つの書き方を混ぜることもできる:

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

辞書を受け取る

関数の定義において最後の引数を **name の形で書くと、キーワード引数の辞書を受け取ることができる。

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

呼び出しは

>>> 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'}}

のようにできる。ただしキーワードを付けないと

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

のようなエラーになるのでそこだけ注意すること。

ラッパーでのパラメータの引き回し

例えば、 requests.get()

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

をラップして body を返す関数

import requests

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

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

があったとして、timeout を指定したいとなったときに

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))

としてもよいが、これだと将来 headers とか proxies も指定したいとなったときに引数を増やして回る必要が出てしまう。一方、

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))

のようにしておくとその必要がない(引数の仕様を知るには中で呼んでいるライブラリ関数の仕様を見ないといけなくなるので、何をラップしているかの情報がわかりやすいようにしておく必要はある)。ちなみに例で使った requests.get 自体も requests.request のラッパーになっていて、上と同じ方法でパラメータの引き回しがされている。

参考文献

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?