【Python】関数の引数の * と ** って何?(可変長引数)

Pythonの引数の*と**についての解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Python の関数の引数に利用される *** について説明していきます。

自分自身では利用する機会は少ないかもしれませんが、Python の標準モジュールや外部モジュールのソースコードを見てみると下記のような引数を持つ関数が定義されていることが多いです。

*argsと**kwargs
def func(arg1, *args, **kwargs):
    pass

*** の意味合いを知らない場合、上記のような関数定義を見ると混乱してしまうと思います。ですが、このページを読んで *** の意味合いを理解していただければ、上記のような関数定義を見た際に内容がすんなり理解できるようになります。また、*** の意味合いを理解していただくことで、自分自身で *** を使いこなすこともできるようになります!

仮引数における ***

まず前提として、引数には2つの種類があります。仮引数と実引数です。仮引数は関数定義に使用される引数のことです。関数定義時には「この関数ではこれらの引数を受け取りますよー」ということを定義しておく必要があります。これらが仮引数となります。

仮引数の説明図

それに対し、関数呼び出し時に指定する引数は実引数と呼ばれます。簡単に言えば、関数の引数に実際に渡す値のことになります。

実引数の説明図

Python においては仮引数の引数名に *** を指定することが可能ですし、実引数として指定する変数名に *** を指定することも可能です。ですが、*** の意味合いは仮引数と実引数で全く異なります。逆の意味合いになると考えても良いです。

では、どういった意味合いになるのか?

この点について、まずは仮引数の方から説明していきたいと思います。

MEMO

このページでは *引数名**引数名 などの 引数名 の左側に *** が付加された場合の仮引数について説明していきます

下記のように、単に * のみを仮引数として指定した場合、「* よりも右側の引数はキーワード引数として指定しなければならない」ことを示す記述となります

*のみの指定
def func(a, b, *, c, d, e):

上記の場合は、a, b は位置引数で指定可能ですが、cde はキーワード引数で指定しなければ関数呼び出し時に例外が発生します

* のみを指定する場合と *引数名 を指定する場合とで意味合いが異なるため、その点にご注意ください

可変長の引数であることを示す記号

仮引数における ***可変長引数であることを示す記号になります。*引数名 や **引数名 が指定されている場合、それぞれ下記のような可変長引数を受け取ることを示す仮引数となります。

  • *引数名:可変長の位置引数
  • **引数名:可変長のキーワード引数

この “可変長” とは、関数呼び出し時に指定可能な引数の数が可変であることを示す言葉になります。例えば下記のように定義されている関数であれば、1つの位置引数と、可変長の位置引数および可変長のキーワード引数を受け取ることができます。

可変長引数を受け取る関数func
def func(arg1, *args, **kwargs):
    print('args:', args)
    print('kwargs:', kwargs)

    print('type of args:', type(args))
    print('type of kwargs:', type(kwargs))

そのため、下記のようにargs に位置引数4つ、kwargs にキーワード引数3つを指定して関数を呼び出すこともできますし、

funcの呼び出し例1
func(1, 2, 3, 4, 5, a=6, b=7, c=8)

下記のようにargs に位置引数1つ、kwargs にキーワード引数5つを指定して関数を呼び出すこともできます。

funcの呼び出し例2
func(1, 2, a=6, b=7, c=8, d=9, e=10)

さらに、下記のようにargskwargs を指定せずに関数を呼び出すこともできます。受け取れる引数の数が可変長なので、いくらでもこのような例は示すことができます。

funcの呼び出し例3
func(1)

このように、*** は仮引数において可変長の引数を扱うための記号となります。そして、* に関しては可変長の位置引数を、** に関しては可変長のキーワード引数を扱うための記号となります。

なので、*args のような * が指定された仮引数が定義された関数を呼び出す際には、任意の個数の位置引数が指定可能となりますし、**kwargs のような ** が指定された引数を受け取る関数を呼び出す際には任意の個数のキーワード引数が指定可能となります。

まずは、この点を覚えておきましょう!

指定された引数がどのように扱われるかは関数によって異なりますが、親切なモジュール等であればドキュメントに扱われ方が記載されていることも多いです。

スポンサーリンク

関数が受け取るのはパックされたデータ

次はもう少し踏み込んで仮引数における *** について説明していきます。関数呼び出し側からすれば可変長の引数を指定してやれば良いことになることは先ほど説明しました。では、関数側では、*** が指定された引数はどのように扱われるのでしょうか?

これは、先ほども示した下記のような func 関数を実際に実行してみると分かりやすいです。

可変長引数を受け取る関数func
def func(arg1, *args, **kwargs):
    print('args:', args)
    print('kwargs:', kwargs)

    print('type of args:', type(args))
    print('type of kwargs:', type(kwargs))

func(1, 2, 3, 4, 5, a=6, b=7, c=8)

上記のスクリプトを実行すれば、func 関数の可変長引数である *args に4つの引数が、**kwargs に3つの引数が指定されることになります。func 関数では、これらの argskwargs そのもの、および、これらの型を出力するようにしています。実際に実行すれば、下記のような出力結果が得られると思います。

args: (2, 3, 4, 5)
kwargs: {'a': 6, 'b': 7, 'c': 8}
type of args: <class 'tuple'>
type of kwargs: <class 'dict'>

すごく分かりやすい結果が出力されましたね!

この結果からも分かるように、args はタプル型であり、kwargs は辞書型のデータとなります。つまり、関数呼び出し時に指定したバラバラの位置引数が1つのタプルにパックされ、さらにバラバラのキーワード引数が1つの辞書にパックされた状態で関数に渡されることになります。

可変長引数が1つの引数としてパックされて関数に渡される様子

そして、これらは普通のタプル型・辞書型のデータとなるので、通常のタプルや辞書の使い方で利用することが可能です。

タプルや辞書を受け取るためのものではない

ここまでの話は分かりやすいのではないかと思います。でも、ここで次のような疑問が湧いてくるのではないでしょうか?

結局関数がタプルや辞書を受け取るのであれば、引数にタプルや辞書を直接指定してもいいんじゃね?

要は、例えば上記のような func 関数を呼び出す際に次のような引数の指定の仕方でも問題ないのではないか?と言う疑問になります。

可変長引数としてタプルや辞書を渡す1
touple_data = (2, 3, 4, 5)
dict_data = {
    'a': 6, 'b': 7, 'c': 8
}

func(1, touple_data, dict_data)

これは実際に実行してみれば分かります。実行した時の出力結果は下記のようになります。

args: ((2, 3, 4, 5), {'a': 6, 'b': 7, 'c': 8})
kwargs: {}
type of args: <class 'tuple'>
type of kwargs: <class 'dict'>

あらら…。argstouple_datadict_data を受け取ってしまいましたね…。そしてこれらが1つのタプルの中に格納される形になってしまっています。

まぁ、これは冷静に考えれば当然の結果になります。touple_data も dict_data も両方とも位置引数で指定しているので、func 関数はこれらがパックされたデータを args として受け取ることになります。

じゃあ、ちょっと作戦を変えて下記のように kwargs になんとかして辞書データを渡すようにしてみましょう!

可変長引数としてタプルや辞書を渡す2
touple_data = (2, 3, 4, 5)
dict_data = {
    'a': 6, 'b': 7, 'c': 8
}

func(1, touple_data, kwargs=dict_data)

この場合の結果は下記のようになります。

args: ((2, 3, 4, 5),)
kwargs: {'kwargs': {'a': 6, 'b': 7, 'c': 8}}
type of args: <class 'tuple'>
type of kwargs: <class 'dict'>

この場合も結果は意図したものではないですね…。この場合、func 関数は kwargs というフィールドの値に dict_data がセットされた辞書を kwargs として受け取ることになってしまっています。また、args 側も “1つのタプル” が要素であるタプルを受け取ってしまっています。

こういった動作からも分かるように、仮引数で指定される *** は、あくまでも関数呼び出し側が可変長引数を指定可能にするためのものとなります。関数側で受け取るデータの型がタプルや辞書であるからといって、関数呼び出し時にタプルや辞書を引数に指定すると意図した通りの動作にならないので注意してください。

では、この前提に基づき、次の2つの関数 main_funcsub_func について考えてみましょう。この main_func から sub_func を呼び出す際に、引数として main_func が受け取った argskwargs の各引数をそのまま sub_func の引数に指定したいとします。

main_funcとsub_func
def sub_func(arg1, *args, **kwargs):
    print('args:', args)
    print('kwargs:', kwargs)

    print('type of args:', type(args))
    print('type of kwargs:', type(kwargs))

def main_func(arg1, *args, **kwargs):
    sub_func(引数)

main_func(1, 2, 3, 4, 5, a=6, b=7, c=8)

この時、main_func が受け取った args をそのまま sub_funcargs 引数で受け取り、main_func が受け取った kwargs をそのまま sub_funckwargs 引数で受け取れるようにするには、どのようにして main_funcsub_func を呼び出せば良いのでしょうか?

受け取った可変長引数を他の関数に渡す様子

この例のように、可変長引数を受け取った関数が他の関数にそのままその可変長引数を渡したいようなケースは実際の開発でも多々あります。

ちなみに、先ほどの例でも説明したように、下記のように引数を指定して sub_func を実行するのではダメです。この場合、sub_funcmain_func が受け取った argskwargs をパックしたものを args 引数で受け取ることになります。逆に kwargs は1つも引数を受け取れません。

引数のNGの指定例
def main_func(arg1, *args, **kwargs):
    sub_func(arg1, args, kwargs)

もしかしたらいろんな方法があるかもしれませんが、最も簡単な方法は sub_func を呼び出す際に *** を利用することになると思います。つまり、下記のように sub_func の実引数に *args**kwargs を指定します。

実引数をアンパックする
def main_func(arg1, *args, **kwargs):
    sub_func(arg1, *args, **kwargs)

では、このように実引数に指定した場合、*** はどのように作用するのでしょうか?

それについて、次の章で解説していきます。

実引数における ***

では、続いて実引数における *** について説明します。

仮引数における * と ** の最初にも説明しましたが、”実引数における ***” は “仮引数における ***” とは逆のような意味を持つ記号となります。

前述した通り、*** が指定された仮引数は、それぞれ可変長の位置引数と可変長のキーワード引数を受け取る引数となります。そして、関数呼び出し時に実引数として指定された可変長の引数は、1つのタプルおよび1つの辞書にパックされて関数に渡されることになります。

スポンサーリンク

アンパックを命令する記号

それに対し、関数呼び出し時に指定する実引数に指定するリストやタプル等のデータに  * を指定した場合、 それらのデータの各要素がアンパックされて、それぞれ個別の位置引数として関数に指定されることになります。

*によってタプルがアンパックされる様子

同様に、関数呼び出し時に指定する実引数に指定する辞書のデータに  ** を指定した場合、 その辞書のデータの各要素がアンパックされて、それぞれ個別のキーワード引数として関数に指定されることになります。

**によって辞書がアンパックされる様子

つまり、実引数における  *** はアンパックを命令する記号となります。

前述の通り、仮引数における *** の場合、可変長引数を受け取る際にそれぞれの引数がパックされて渡されることになります。実引数においてはアンパックされ、仮引数においてはパックされることになるため、この点に注目すれば *** は実引数に指定される場合と仮引数に指定される場合とで逆の意味合いになります。

タプルや辞書を可変長引数として指定することが可能

さて、ここで 仮引数における * と **  でも示した下記の例について考えてみましょう。

main_funcとsub_func
def sub_func(arg1, *args, **kwargs):
    print('args:', args)
    print('kwargs:', kwargs)

    print('type of args:', type(args))
    print('type of kwargs:', type(kwargs))

def main_func(arg1, *args, **kwargs):
    sub_func(arg1, *args, **kwargs)

main_func(1, 2, 3, 4, 5, a=6, b=7, c=8)

ここで、main_funcsub_func を呼び出す際に、引数がどのようにして扱われるのかについて説明したいと思います。

まず、main_func 関数の引数の定義により、この関数は1つの位置引数(arg1)、可変長の位置引数(args)、可変長のキーワード引数(kwargs)を受け取ることになります。

上記のように main_func 関数を実行すれば、1arg1 として、2345 がタプルにパックされた (2, 3, 4, 5)args として、a=6b=7c=8 が辞書にパックされた {'a': 6, 'b': 7, 'c': 8} を kwargs として main_func が受け取ることになります。

指定された可変長引数がパックされて関数に渡される様子

さらに、 main_funcsub_func を呼び出す際には下記のように実引数が指定されています。

実引数をアンパックする
def main_func(arg1, *args, **kwargs):
    sub_func(arg1, *args, **kwargs)

この場合、関数が呼び出される前に、*** の命令が実行されることになります。*args では args をリストやタプル等からアンパックして各要素に分解する処理が実施されます。引数 args は (2, 3, 4, 5) を受け取っているため、このアンパックにより、sub_func 呼び出し部分は下記のように変化することになります。

*argsの実行結果
def main_func(arg1, *args, **kwargs):
    sub_func(arg1, 2, 3, 4, 5, **kwargs)

さらに **kwargs では kwargs を辞書からアンパックして各要素に分解する処理が実施されます。引数 kwargs{'a': 6, 'b': 7, 'c': 8} を受け取っているため、このアンパックにより、sub_func 呼び出し部分は下記のように変化することになります。

**kwargsの実行結果
def main_func(arg1, *args, **kwargs):
    sub_func(arg1, 2, 3, 4, 5, a=6, b=7, c=8)

つまり、*** の命令により、sub_func 関数呼び出し時の引数は main_func 関数呼び出し時に指定した引数と同じものになります。さらに、sub_func 関数の引数定義は main_func 関数と全く同じですので、main_func 関数呼び出し時と同じ動作によって、1arg1 として、2345 がタプルにパックされた (2, 3, 4, 5)args として、a=6b=7c=8 が辞書にパックされた {'a': 6, 'b': 7, 'c': 8} を kwargs として sub_func が受け取ることなります。

アンパックにより可変長引数として分解されて指定される様子

このように、可変長引数としてタプルや辞書を受け取った関数から他の関数に全く同じ可変長引数を指定することは、関数呼び出し時に指定する実引数に対して *** を指定することで簡単に実現することが可能です。

もし、Python が実引数に対する *** の指定によってアンパックする機能を持っていないのであれば、上記と同様の動作を実現するためにプログラマーは引数で受け取ったタプルや辞書を自力でアンパックする必要があります。これは面倒です…。

ですが、Python が実引数に対する *** の指定によってアンパックする機能を備えているため、タプルや辞書から可変長引数への変換が簡単に行うことができるようになっています。

この仮引数における *** と実引数における *** の意味合いを理解しておけば、可変長引数を扱うサンプルコードも混乱することなく読めるようになりますし、自身で可変長引数を扱う関数やメソッドも開発しやすくなります。

単なるパック・アンパック機能として利用も可能

ここからは参考情報になります。

ここまで、関数の仮引数や実引数に指定する場合の *** について説明していきましたが、実は関数の仮引数と実引数に指定する時以外でも *** を利用することが可能で、単なるパック・アンパック機能としてこれらを利用することもできます。

例えば下記が実行されれば、xy12 を参照するようになりますが、z に関しては残りの 345 をパックして生成されたリスト [3, 4, 5] を参照するようになります。

パック機能の利用
x, y, *z = 1, 2, 3, 4, 5

* を利用せずに下記のように記載した場合、左辺の変数の数と右辺の値の数が合わないので例外が発生することになります。

パック機能なしの場合
x, y, z = 1, 2, 3, 4, 5

このように、*** を単なるパック・アンパックする機能として利用することも可能です。

ですが、一番利用する機会が多いのは、ここまでも説明してきたような「関数の仮引数や実引数に指定する場合」になると思います。そして、これらを利用する目的は可変長引数の受け取りや受け渡しを実現することになります。

ということで、*** に関しては、まずはこれらが可変長引数を実現するためのものであり、これが実現可能なのは *** にパック・アンパック機能があるためであることを覚えておくくらいで良いと思います!

スポンサーリンク

まとめ

このページでは、Python における *** について説明しました!

特に関数の仮引数に *引数名**引数名 が存在する場合、その関数は可変長引数を受け取ることが可能となります。そして、タプルやリスト・辞書など、複数のデータがパックされた変数を関数呼び出し時に可変長引数として指定するために、実引数として *変数名 や **変数名 が指定されることになります。これにより、関数呼び出し前に変数がアンパックされて別々の引数として指定されるようになります。

冒頭でも触れたように、Python のコードを読んでいると関数の仮引数や実引数に *** が指定されていることも多いです。これらの意味を知らないとコードを読む際に混乱することになりますが、今回説明した内容を理解していただければコードの意味合いもスッキリ理解できるようになると思いますので、是非 *** の意味合いについては理解しておきましょう!

同じカテゴリのページ一覧を表示