Pythonの関数-可変長の引数(*args、**kwargs)と引数順番ルール

記事の内容

 この記事では、関数作成の際、引数を*argsや**kwargsと記述することで、関数に渡す引数の数を可変にする方法について説明します。
 そもそも、「関数に渡す引数の数を可変にする」のは、どんな場合なのでしょうか。いくつか実例を上げてみると、、、
 print文は、引数をいくつでも指定できるようになっており、渡された引数をすべてコンソールに出力します。以前の記事で紹介したrange関数は、関数を呼び出す際に渡す引数の数を1~3個指定できました。
 また、元の関数を修飾するデコレータでも可変長引数を利用します(デコレータの詳細については次の記事参照)。

***わからない用語があるときは索引ページへ***

1.*argsの使い方

 まずは*argsの使い方について、サンプルプログラムを使って説明します。

##args_ex1.py

##引数に*argsと指定することにより、指定された引数が変数名argsの
##タプルオブジェクトに代入される
def get_average(*args):
   ##引数の合計値を求める
   sum = 0
   for x in args:
       sum += x
   
   ##引数の個数を数える
   elem_num = len(args)
   
   ##平均を計算して返す
   average = sum / elem_num
   
   return average


##プログラム実行開始位置
average1 = get_average(80, 65, 40, 90, 75)
average2 = get_average(90, 75, 55)

print("平均1:", average1)
print("平均2:", average2)

 Pythonでは、関数宣言部のdefの引数に、*argsと記述すると、関数呼び出しの際に渡される1つ以上の引数を、変数名argsのタプルオブジェクトに代入します。
 つまり、"args_ex1.py"のサンプルプログラムでは、

average1 = get_average(80, 65, 40, 90, 75)

で、get_averageに渡された5つの数を、get_average関数の作成部では、タプルオブジェクト

args = (80, 65, 40, 90, 75)

として受け取っています。
出力結果

平均1: 70.0
平均2: 73.33333333333333

ここで、大事なのは引数の変数名argsの前の"*"(アスタリスク)です。関数宣言部のdefで、引数を

def <関数名>(*<変数名>)

とすることで、関数呼び出しの引数のセットは<変数名>で指定した名前のタプルオブジェクトに渡されます。つまり、変数名は"args"である必要はありません。ただ、慣例として"args"という変数名を使っているだけです

2.**kwargsの使い方

 つづいて**kwargsの使い方について、サンプルプログラムを使って説明します。

##引数に**kwargsと指定することにより、指定された引数が変数名kwargsの
##辞書オブジェクトに代入される
def kwargs_test(**kwargs):
   print(kwargs)
   
##プログラム実行開始位置
kwargs_test(x = 5, y = 3)
kwargs_test(s1 = "a", s2 = "b", s3 = "c")

 Pythonでは、def文の引数に、**kwargsと記述すると、関数呼び出しの際に、代入文形式で渡される1つ以上の引数を、変数名kwargsの辞書オブジェクトに代入します。

{
    "<変数名1>": <引数1>,
    "<変数名2>": <引数2>,
        :         :
}

 代入文形式で引数を渡す関数の呼び出しは、引数にデフォルト値が設定された関数の引数を渡すときによく使われます。
出力結果:

{'x': 5, 'y': 3}
{'s1': 'a', 's2': 'b', 's3': 'c'}

 変数名kwargsは慣例として使っているだけなので、*args同様、

 **<変数名>

であればよく、**kwargs以外の変数名も使えます

3.引数の順番ルール

 ここまで説明してきたように、def文での関数宣言では、4種類の引数の定義方法があります。
 (1)変数のみの引数
 (2)*args引数
 (3)デフォルト値を設定した引数
 (4)**kwargs引数
の4種類です。

 関数呼び出しでセットされた引数を、関数本体で定義された引数に正しく渡すために、これら4種類の引数が混在する場合は、定義する順番にルールがあります

 優先順位1:変数のみの引数
 優先順位2:*args引数
 優先順位3:デフォルト値が設定された引数
 優先順位4:**kwargs引数

 以下、関数の引数定義の例です。

def function(a, b, *args, **kwargs):

 次に関数の呼び出し側をみてみましょう。
 上記の関数宣言での引数の順番ルールにより、関数の呼び出しは、必然的に、

function(a, b, c, x=data1, y=data2)

のように、データのみ渡す引数を先に渡し、変数にデータを代入する形式の引数を後ろに書く形式となります
 このfunctionの例では、それぞれ
 a=a, b=b, args=(c), kwargs={"x": data1, "y": data2}
で一意に引数を渡しています。

 上記順番ルールを踏まえると、関数作成のdef文の宣言で、

def function(*args, **kwargs):

と書くと、この形式は関数呼び出しにおける引数渡しの全パターン網羅した宣言になっています。Pythonでは、関数本体そのものもオブジェクトとして扱うことができます。このパターンは、すべての引数渡しの全パターンを網羅しているため、任意の関数をオブジェクトで扱う際に利用します
 後ろの記事で説明する関数のデコレーションでもこの形を利用しているので、ぜひ覚えておきましょう。

 引数(*args, **kwargs)は、引数なしの呼び出しパターンも、もちろんカバーしています。

function()

で呼び出したときに、関数の定義本体が

def function(*args, **kwargs):

と宣言されていた場合、引数argsには空のタプル()が、引数kwargsには空の辞書{}がセットされます。

3.print文をのぞいてみる

 可変長引数が利用できるprint文の引数の宣言をのぞいてみましょう。

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

 引数*objectsで、出力するオブジェクトを変数名objectsのタプルで受け取っています。出力したいオブジェクトの数を必要分複数指定できるのは、この引数のおかげです。

 次の変数sep=' 'は、出力と出力の区切り文字で、デフォルト値に半角スペースがセットされています。sepを","(カンマ)などに変更すると、出力するオブジェクトを","(カンマ)で区切って出力します。
 いくつか例を見てみましょう。

例1)

print("hello", "world")

出力結果

hello wrold

例2)

print("hello", "world", sep="-")

出力結果

hello-world

 次の変数end='/n'は、出力の最後に追加する文字列で、デフォルト値には改行のエスケープシーケンスがセットされています。endを""(空文字)に設定すると、print文で出力後も改行されないので、次にprint文を使ったときには改行されずに、そのまま前の出力の後ろに続けて出力されます。endには任意の文字列を指定できます。

例3)

print("hello")
print("world")

出力結果

hello
world

例4)

print("hello", end="-")
print("world")

出力結果

hello-world

 file=sys.stdoutは、出力先を標準出力(コンソールに出力すること)に指定しています。詳細は省略。

 flush=Falseは、print文の出力タイミングを指定しています。Python3.3以降で追加された引数です。
 Trueにセットすると、出力先バッファに入力があるたびにリアルタイムに出力していきます。Falseのままの場合はバッファにため込んで、バッファが改行されるか、入力が完了したらまとめて出力されます。
 ストリームデータ(画像データなど)を出力したり、プログラム処理の進捗をバーや"*"などで出力する際にあえてTrueで指定することが多いようです。

---以下、補足---
 Pythonのprintは、実は関数(def)ではなく、クラス(class)で定義されています。メソッドを使わない限り呼び出し方は関数と全く同じですので、よく利用されるprint文で説明しました。
---補足ここまで---

前の記事 次の記事


いいなと思ったら応援しよう!