Pythonの*argsと**kwargsを使った可変長引数

関数には引数を持たないものもあれば、複数の引数を持つものもあります。

関数には引数のないものもあれば、複数の引数を持つものもあります。

他の開発者に柔軟なAPIを提供したい、入力サイズが分からないなどの理由で、引数の数を可変にすることがあります。

Pythonでは、任意の数の引数を受け取る関数を作成することができます。

この記事では、可変長の引数を持つ関数を定義して使用する方法について見ていきます。

これらの関数は、連続したエントリや名前付き引数として、未知の量の入力を受け入れることができます。

*args で多くの引数を使用する。

2つの数値の間の最小値を求める関数を実装してみましょう。

以下のような感じです。

def my_min(num1, num2):
    if num1 < num2:
        return num1
    return num2


my_min(23, 50)


23


これは、最初の数字が2番目の数字より小さいかどうかをチェックするだけです。

もし小さければ、最初の数字が返される。

そうでなければ、2番目の数字が返される。

もし3つの数の最小値を求めるなら、my_min()にもう一つ引数を追加し、さらにifステートメントを追加すればよいでしょう。

最小化関数が不特定多数の中から最小の数を求める必要がある場合は、リストを使用することができます。

def my_min(nums):
    result = nums[0]
    for num in nums:
        if num < result:
            result = num
    return result


my_min([4, 5, 6, 7, 2])


2


これはうまくいきますが、コーダーは数字をリストで囲む必要があり、2つか3つの引数を定義していたときほど簡単ではありません。

そこで、可変長の引数を使って、両方の長所を生かそうというわけだ。

可変長引数、略してvarargsは、不特定多数の入力を受けることができる引数である。

これを使うと、プログラマはデータをリストや代替シーケンスで包む必要がありません。

Pythonでは、可変長引数は *args という構文で定義されます。

それでは、 my_min() 関数を *args で再実装してみましょう。

def my_min(*args):
    result = args[0]
    for num in args:
        if num < result:
            result = num
    return result


my_min(4, 5, 6, 7, 2)


2


注: args は単なる名前です。

アスタリスク (*) が1つ付いていれば、その vararg にどんな名前を付けてもかまいません。

すぐにわかるように args という名前をつけておくのがベストプラクティスです。

argsの後に来る引数はすべて名前付き引数でなければなりません。

つまり、位置ではなく名前で参照される引数です。

args を使用すると、他の引数を位置で参照することができなくなります。

また、一つの関数の中では *args 型の vararg しか持つことができません。

あなたは、 *args を使った解決策はリストの解決策と非常に似ていると思うかもしれません。

これは、 *args が内部的には Tuple であり、リストと同様に反復可能なシーケンスであるためです。

もし、この型を確認したい場合は、Pythonのインタープリターでコードを入力してください。

$ python3
>>> def arg_type_test(*args):
...     print(type(args))
...
>>> arg_type_test(1, 2)
<class 'tuple'=""


my_min()のように、*args` を使うと、複数の引数を順番に受け付けることができます。

これらの引数は、その位置によって処理されます。

もし、複数の引数を受け取りたいが、引数の名前で参照したい場合はどうすればよいだろうか?次のセクションで、その方法について見ていきましょう。

Using Many Named Arguments with **kwargs

Python は **kwargs として知られる複数のキーワード引数を受け取ることができます。

この機能は *args と似ていますが、引数はタプルではなく辞書に格納されます。

def kwarg_type_test(**kwargs):
    print(kwargs)


kwarg_type_test(a="hi")
kwarg_type_test(roses="red", violets="blue")


出力は次のようになります。

{'a': 'hi'}
{'roses': 'red', 'violets': 'blue'}


辞書を使用することで、 **kwargs は引数の名前を保持することができるが、引数の位置を保持することはできない。

注意: args と同様に、 kwargs 以外の名前を使用することもできます。

しかし、ベストプラクティスでは、一貫して kwargs を使用するように指示されています。

***kwargsは辞書なので、.items()` メソッドを使用して、他の辞書と同様に反復処理することができる。

def kwargs_iterate(**kwargs):
    for i, k in kwargs.items():
        print(i, '=', k)


kwargs_iterate(hello='world')


実行すると、コンソールに次のように表示されます。

hello = world


キーワード引数は、引数が利用可能かどうかわからない場合に便利です。

例えば、ブログの記事をデータベースに保存する関数があった場合、内容や作者のような情報を保存します。

ブログの記事にはタグやカテゴリーがあるかもしれませんが、それらは常に設定されているわけではありません。

このような関数を定義することができる。

def save_blog_post(content, author, tags=[], categories=[]):
    pass


あるいは、関数の呼び出し側で任意の数の引数を渡すことができ、tagscategories が設定されている場合のみ関連付けることができます。

def save_blog_post(content, author, **kwargs):
    if kwargs.get('tags'):
        # Save tags with post
        pass


if kwargs.get('categories'):
        # Save categories with post
        pass


さて、可変長の引数をサポートする両方のタイプを把握したところで、この2つを1つの関数にまとめる方法を見てみましょう。

Varargsとキーワード引数の組み合わせ

Pythonのライブラリや再利用可能なコードを書くときに、 *args**kwargs を一緒に使いたいことがよくあります。

幸運なことに、 *args**kwargs はうまく組み合わせて使うことができる。

def combined_varargs(*args, **kwargs):
    print(args)
    print(kwargs)


combined_varargs(1, 2, 3, a="hi")


このコードスニペットを実行すると、次のようになります。

(1, 2, 3)
{'a': 'hi'}


位置引数と名前付き引数を混在させる場合、位置引数は名前付き引数の前に来なければならない。

さらに、固定長の引数は可変長の引数の前に来る。

したがって、次のような順序になる。

  1. 既知の位置決め引数
  2. 引数 *args
  3. 既知の名前付き引数
    1. **kwargs

すべてのタイプの引数を持つ関数は次のようになります。

def super_function(num1, num2, *args, callback=None, messages=[], **kwargs):
    pass


この順序に従って、可変長引数やキーワード引数を持つ関数を定義し、呼び出すと、期待通りの動作が得られるでしょう。

これまで私たちは、関数の定義に *args**kwargs という構文を使ってきました。

Pythonでは、関数を呼び出すときにも同じ構文を使用することができます。

では、その方法を見てみましょう。

Unpacking Arguments with *args and **kwargs

3つの数値を受け取ってその合計を表示する関数 add3() を考えてみましょう。

このように作成します。

def add3(num1, num2, num3):
    print("The grand total is", num1 + num2 + num3)


数値のリストがあれば、どのリストを引数にするかを指定すれば、この関数を使うことができます。

magic_nums = [32, 1, 7]


add3(magic_nums[0], magic_nums[1], magic_nums[2])


このコードを実行すると、次のようになる。

The grand total is 40


これはうまくいくのですが、*args構文を使ってもっと簡潔にすることができます。

add3(*magic_nums)


出力は、前と同じように The grand total is 40 です。

関数の呼び出しで *args 構文を使用する場合、変数をアンパックしていることになります。

解凍するということは、リストの個々の値を取り出すということです。

この場合、リストの各要素を取り出し、引数に配置します。

位置0が最初の引数に対応します。

同様にタプルも解凍することができる。

tuple_nums = (32, 1, 7)
add3(*tuple_nums) # The grand total is 40


辞書を展開したい場合は、**kwargs構文を使用しなければなりません。

dict_nums = {
    'num1': 32,
    'num2': 1,
    'num3': 7,
}


add3(**dict_nums) # The grand total is 40


この場合、Pythonは辞書のキーと引数名をマッチさせ、その値を設定します。

これで完了です。

オブジェクトから値を必要とする引数をそれぞれ指定する代わりに、値をアンパックすることで関数呼び出しをより簡単に管理することができます。

結論

Python では、 *args**kwargs という構文を使って、関数の引数を可変個数で捕捉することができます。

argsを使用すると、関数の位置で不特定多数の引数を処理することができます。

また、**kwargs` を使用すると、引数の名前から不特定多数の引数を取得することができる。

関数はそれぞれ可変長の引数を1つだけ持つことができるが、1つの引数に両方のタイプの関数を組み合わせることができる。

その場合、位置決め引数が名前付き引数の前に来るようにし、固定引数が可変長の引数の前に来るようにしなければなりません。

Pythonでは、関数呼び出しのための構文も使うことができます。

もしリストやタプルを持っていて *args シンタックスを使うと、それぞれの値を位置引数として解凍します。

辞書があり、**kwargs構文を使用すると、辞書のキーの名前と関数の引数の名前をマッチングさせます。

このようなタイプの引数の恩恵を受けられるような関数を作っていますか?あるいは、関数をリファクタリングして、将来的に使えるようにできるかもしれませんね?あなたが取り組んでいるものを教えてください。

</class

タイトルとURLをコピーしました