背景
Pythonコードを書いていると関数に*args, **kwargsという引数を使う場面に出くわすと思います。
初見だと一体これは何をやっているの??と感じたため、まとめてみます。
本文
*args, **kwargsとは何なのか?結論
引数の大きさを自由に変更できる引数のこと。
詳しく説明します。引数が固定長の場合、例えば2つの入力のみ想定していたが、3つや4つの入力の合計を出力したいとなった時、新たに3引数、4引数の関数を作るのは面倒ですよね。
そこで*argsや**kwargsが登場します。
*argsでは複数の引数をタプルとして受け取り、**kwargsでは複数のキーワード引数を辞書として受け取ります。これらは可変長引数と呼ばれているので覚えておきましょう。
ちなみにargsとかkwargsって何なの?って思う方いると思います。おそらくですが、
args: argments
kwargs: keywords argments
の略だと思います。知っている方いらっしゃれば教えてください。
それではサンプルコードを見てみます。
def func_args(*args):
return sum(args)
print(num_sum(1, 2, 3))
# 6
print(num_sum(1, 2, 3, 4, 5, 6))
# 21
def func_kwargs(**kwargs):
print('kwargs: ', kwargs)
func_kwargs(key1=1, key2=2, key3=3)
# kwargs: {'key1': 1, 'key2': 2, 'key3': 3}
確かに引数を何個も受け取れてますね。非常に便利な機能であることが分かります。
注意点
可変長引数は非常に便利な機能ですが、何でも使えばいいわけではありません。具体的に問題例を見てみます。
class User:
def __init__(self, **kwargs):
self.name = kwargs['name']
self.mail = kwargs.get('mail')
user = User(name="hoge", address="hogehoge") # エラーが起きない
print(user.mail)
# None
上記のコードでは、クラスが期待していないaddress=
も引数として受け取れてしまいます。さらにuser.mail
はNoneになるため、別のプログラム上でエラーになる可能性があります。
実用を考えると注意が必要ですね。
もう一つ例を見てみます。
def hoge(a, b, *args):
print(a)
print(b)
print(args)
a="Hello!"
b="World!"
c=["I am a", "nooby", "programmer!!"]
hoge(a,b,c)
# Hello!
# World!
# (['I am a', 'nooby', 'programmer!!'],)
可変長引数は位置引数と組み合わせることもできます。この場合、位置引数より後ろ側で指定した値がargs
にタプルとして渡されます。
この時、位置引数のみ記述すればどうなるでしょうか。
hoge(a, c)
# Hello!
# ['I am a', 'nooby', 'programmer!!']
# ()
args
には空のタプルが入力されています。これは可変長引数が自由な長さを持てることにより起こります。エラーの原因になりそうですね。
このままだとよくないので解決策として、
def hoge(a,b,*args):
if len(args)==0: raise TypeError("args is not given") # argsがタプル型なのを利用する
print(a)
print(b)
print(args)
a="Hello!"
b="World!"
c=["I am a","nooby","programmer!!"]
hoge(a,c)
# TypeError: args is not given
こうすることで引数が想定より少ないとき、エラーコードを出力できるようになりました。
参考サイト
https://note.nkmk.me/python-args-kwargs-usage/
https://itstudio.co/2019/08/05/9454/#toc10
https://pg-chain.com/python-function-args
https://jisou-programmer.beproud.jp/関数設計/9-関数の引数に可変長引数を乱用しない.html
https://qiita.com/heat_exchange/items/ad730e02f429992b06dd