関数

【Python】関数の定義と呼び出し方法の基本(位置引数、キーワード引数、デフォルト引数、*args、**kwargs)

【Python】関数の定義と呼び出し方法の基本(位置引数、キーワード引数、デフォルト引数、_args、__kwargs)

Pythonで関数を作成し定義する方法引数の種類や使い方位置引数キーワード引数デフォルト引数*args**kwargs)について解説します。

関数の定義方法

基本的な関数宣言方法

Pythonで繰り返しいろいろなところから使用される処理を定義する場合には、関数で定義し、様々なところから呼び出すことで繰り返し利用します。

関数は、defを使って以下のように定義します。defは「定義する」の意味のdefineからきた略となっています。

# 関数定義の基本
def print_hello_world():
    print("hello world!")


if __name__ == "__main__":
    print_hello_world()
【実行結果】
hello world!

上記は、print_hello_world関数は、呼び出されたら’hello world!’とprintするだけの簡単な関数です。呼び出す際には、print_hello_world()といった形で処理を呼び出すことができます。

「if __name__ == ‘__main__’:」の部分は、ひとまず最初のうちはメイン処理を記載するときの決まり文句というぐらいに思ってもらえれば結構です。プログラムの処理は、この部分から始まります。

関数の引数と返り値の宣言方法

関数は、処理の中で使うための情報である引数を呼び出し元から受け取ったり、処理した結果を返り値として呼び出し元へ返却することができます。

以下の例で見ていきましょう。

# 関数定義 引数、返却値
def func_2(num):
    return 2 * num


if __name__ == "__main__":
    x1 = 5
    y1 = func_2(x1)
    print(f"x1 = {x1}, y1 = {y1}")

    x2 = 10
    y2 = func_2(x2)
    print(f"x2 = {x2}, y2 = {y2}")
【実行結果】
x1 = 5, y1 = 10
x2 = 10, y2 = 20

上記の関数func_2は受け取った数値を2倍して返却する関数です。

関数の()内に引数となる変数を記載すると、関数内の処理で使用することができます。呼び出し元の入力を一時的に関数内の変数にコピーすることから、仮引数とも呼ばれます。一方で関数を呼び出す際の入力も引数と呼ばれ、仮引数と区別する際には実引数と呼びます。

処理を実行した結果を呼び出し元に返す場合には、return文で返却値を記載します。変数を指定してもよいですし、式を指定すれば、その式が評価された結果が返却されます。

関数を呼び出す際には「y1 = func_2(x1)」のような形で指定することができ、x1の値が関数の仮引数のnumに渡されて処理が実行されます。結果として、x1を2倍した値が、y1に返却されて設定されるわけです。

関数の使い方の基本的な部分については以上です。関数の引数には、いくつか種類があるため、以降では関数の引数の種類と使い方(呼び出し方)について説明します。

関数の引数の種類と使い方

位置引数

関数の引数の種類として「位置引数」があります。

以下の例で説明します。このmake_profile関数は、入力された名前(first name)、苗字(last name)、誕生日(birthday)を使って、辞書を生成して返却するシンプルな関数です。

def make_profile(first, last, birth):
    return {"first name": first, "last name": last, "birthday": birth}


if __name__ == "__main__":
    first_name = "Taro"
    last_name = "Python"
    birthday = "2021/1/1"

    # 位置引数にて指定
    profile = make_profile(first_name, last_name, birthday)
    print(profile)

    profile = make_profile(last_name, first_name, birthday)
    print(profile)
【実行結果】
{'first name': 'Taro', 'last name': 'Python', 'birthday': '2021/1/1'}
{'first name': 'Python', 'last name': 'Taro', 'birthday': '2021/1/1'}

位置引数は、引数の位置と順番が重要になってきます。

例えば、上の例だと2回make_profile関数を呼んでいますが、2回目に呼び出す際には、fist_nameとlast_nameの変数の指定が入れ替わっています。その結果、名前であるTaroが苗字として処理されてしまっています。

つまり、関数を呼び出した際の順番通りに関数の仮引数に渡して処理されるのが位置引数です。この場合は、関数を呼び出す際に入力する引数の順序を間違えないように注意して指定する必要があります。

キーワード引数

関数の引数の種類として「キーワード引数」があります。

以下の例で説明します。この例の関数は位置引数の説明と同様の関数で、入力値を辞書にして返却します。

def make_profile(first, last, birth):
    return {"first name": first, "last name": last, "birthday": birth}


if __name__ == "__main__":
    first_name = "Taro"
    last_name = "Python"
    birthday = "2021/1/1"

    # キーワード引数にて指定
    profile = make_profile(birth=birthday, last=last_name, first=first_name)
    print(profile)
【実行結果】
{'first name': 'Taro', 'last name': 'Python', 'birthday': '2021/1/1'}

この例のように、呼び出し時に「仮引数名=」という形で指定する引数をキーワード引数と呼びます。

位置引数の時には、呼び出し元でどういった順番で指定するかが重要でしたが、キーワード引数では呼び出し時に仮引数を指定するため、順番は関係なくなります。

上の例ではbirthday, last_name, first_nameというように、位置引数の順序とは逆順で指定しているにも関わらず、ちゃんとTaroは名前(first name)として処理がされているのが分かると思います。

引数のデフォルト値

関数は引数になっているものを呼び出し元で全て設定しないとエラーになります。

しかし、関数定義の時に引数のデフォルト値を定義しておくと、呼び出し元で引数の指定がなかった場合に、デフォルト値を代わりに使用することができます。

# デフォルト値を指定
def make_profile(first=None, last=None, birth=None):
    return {"first name": first, "last name": last, "birthday": birth}


if __name__ == "__main__":
    first_name = "Taro"
    last_name = "Python"
    birthday = "2021/1/1"

    # キーワード引数にて指定
    profile = make_profile(first=first_name, birth=birthday)
    print(profile)

    profile = make_profile(first=first_name, last=last_name, birth=birthday)
    print(profile)
【実行結果】
{'first name': 'Taro', 'last name': None, 'birthday': '2021/1/1'}
{'first name': 'Taro', 'last name': 'Python', 'birthday': '2021/1/1'}

上記の例のように関数の引数のところで「仮引数=デフォルト値」という形で定義します。この例では、入力がなかった場合にはNoneを設定するようにしています。

もちろん呼び出し元で引数として設定されている場合には、指定した値が関数の仮引数に設定されます。

なお、デフォルト値を設定した引数は、デフォルト値が設定されていない引数の後に定義する必要があります。例えば、「def make_profile(first=None, last, birth):」のように定義するとエラーになりますので注意しましょう。

*argsによる位置引数のタプル受取

基本的な使い方

関数の引数に任意個の引数を渡したい場合には、仮引数に*を付けて*argsのように定義します。仮引数の名前に制約があるわけではありませんが、Pythonを使っている人は通例としてargsという仮引数名をよく使います(argumentsの意味です)。

※ C、C++言語の経験者の方は「*」を見るとポインタを思い出すかもしれませんが、Pythonではポインタという考え方はありません。

# *argsによる位置引数の受け取り
def print_input(*args):
    print(type(args))
    for i, arg in enumerate(args):
        print(f"arg[{i}] = {arg}")


if __name__ == "__main__":
    print_input(1, 2, "A", "B", [1, 2])
【実行結果】
<class 'tuple'>
arg[0] = 1
arg[1] = 2
arg[2] = A
arg[3] = B
arg[4] = [1, 2]

*argsとして指定すると、受け取った引数はタプルとして値が設定されます。(上の例でtype(args)にて型を確認し、<class ‘tuple’>となっていることからも分かります。)

通常の引数との組み合わせ

*argsは、以下のように普通の引数とあわせて使用することもできます。

def print_input(input1, input2, *args):
    print(input1)
    print(input2)
    for i, arg in enumerate(args):
        print(f'arg[{i}] = {arg}')


if __name__ == '__main__':
    print_input(1, 2, 'A', 'B', 'C')
【実行結果】
1
2
arg[0] = A
arg[1] = B
arg[2] = C

上記の例だと、input1、input2には位置引数として指定された順序で指定した値が入り、残りの引数はすべてargsにまとめられていることが分かります。

**kwargsによるキーワード引数の辞書受取

基本的な使い方

関数に任意個のキーと値を指定したい場合には、仮引数に**を付けて**kwargsのように定義します。仮引数の名前に制約があるわけではありませんが、Pythonを使っている人は通例としてkwargsという仮引数名をよく使います(keyword argumentsの意味です)。

# **kwargsによるキーワード引数の辞書受取
def print_input(**kwargs):
    print(type(kwargs))
    for k, v in kwargs.items():
        print(f"key: {k}, value:{v}")


if __name__ == "__main__":
    print_input(opt1=1, opt2=2, value="python")
【実行結果】
<class 'dict'>
key: opt1, value:1
key: opt2, value:2
key: value, value:python

**kwargsとして指定すると、受け取った引数は辞書として値が設定されます。(上の例でtype(kwargs)にて型を確認し、<class ‘dict’>となっていることからも分かります)

通常の引数や*argsとの組み合わせ

**kwargsは、以下のように普通の引数や*argsなどとあわせて使用することができます。

# 通常の位置引数や*argsとの組み合わせ
def print_input(in1, in2, *args, **kwargs):
    print(in1)
    print(in2)
    for i, arg in enumerate(args):
        print(f"arg[{i}] = {arg}")

    for k, v in kwargs.items():
        print(f"key: {k}, value:{v}")


if __name__ == "__main__":
    print_input(1, 2, "A", "B", "C", opt1=1, opt2=2, name="python")
【実行結果】
1
2
arg[0] = A
arg[1] = B
arg[2] = C
key: opt1, value:1
key: opt2, value:2
key: name, value:python

上記ではargs、kwargsそれぞれに値が設定されていることが分かると思います。

ただし、*argsと**kwargsを併用する場合は(*args, **kwargs)の順で並べないといけないので注意しましょう。

関数の引数のデフォルト値に関する注意事項

デフォルト値にはミュータブル(mutable)な型を使うべきではない

関数のデフォルト引数に関して注意しておくべきことがあります。それは「デフォルト値にミュータブル(mutable)な型を使うべきではない。」ということです。

ミュータブル(mutable)な型とは、リストや辞書などのように後から変更することができるような型です。一方で、タプルなどは一度作成したら変更できないのでイミュータブル(immutable)な型といいます。

では、デフォルト引数にミュータブルな型を使うとどのようなことが起こりうるかを例で見ていきます。

def sample_function(in1, tmp_list=[]):
    tmp_list.append(in1)
    return tmp_list


if __name__ == '__main__':
    result1 = sample_function('A')
    print(result1)

    result2 = sample_function('B')
    print(result2)
【実行結果】
['A']
['A', 'B']

この例は、値とリストを受け取ってリストにappendメソッドで追加するような関数になっています。デフォルト値として空リスト[]を設定しており、プログラムを書いた人はリストが指定されていないときは新しくからリストを作成して値を追加してほしいと思っています。

では、呼び出し側を見てみます。2回関数が呼び出されていますがいずれもリストを指定していないので、結果としては1回目は[‘A’]、2回目は[‘B’]が返ってきてほしいわけです。しかし、この例では2回目の呼び出し結果は[‘A’, ‘B’]となっています。

このようになる理由は、関数のデフォルト値は、最初に1回だけ評価されるためです。

2回目以降呼び出されたとしても最初に作成されたリストを使ってしまうため、’B’が追加されて[‘A’, ‘B’]となってしまいます。

このように、リスト等のミュータブルな型をデフォルト値としてしまうと想定外の挙動をしてしまうことがあります。

ミュータブルなデフォルト値を使いたい場合の解決策

では、上記の解決策としてはどのような方法があるでしょうか。答えはシンプルです。以下のようにデフォルト値としてNoneを指定しておき、関数内でNoneだった場合に初期化する処理にすればよいでしょう。

def sample_function(in1, tmp_list=None):
    if tmp_list is None:
        tmp_list = []
    tmp_list.append(in1)
    return tmp_list


if __name__ == '__main__':
    result1 = sample_function('A')
    print(result1)

    result2 = sample_function('B')
    print(result2)
【実行結果】
['A']
['B']

上記のように、引数が渡されなかった時には新たにリストを作成して返却するというような関数にすることで対応できます。

このようにPythonで関数を定義をする際には、リスト、辞書などのミュータブルな型をデフォルト値に指定するとバグにつながりやすいため、ミュータブルな型はデフォルト値に指定しないようにしましょう。

まとめ

Pythonで関数を作成し定義する方法引数の種類や使い方位置引数キーワード引数デフォルト引数*args**kwargs)について解説しました。

関数の定義ではdefを使って、必要に応じて引数や返り値を設定して定義します。

引数では、位置引数、キーワード引数といった考え方があり、*argsや**kwargsといった任意個の引数を渡す仕組みもあります。引数が渡されない時のデフォルト引数を設定することができますが、リスト、辞書などのミュータブルな型を設定するとバグにつながりやすいため注意しましょう。

関数の定義は、プログラミングでも重要なので基本をしっかり押さえましょう。パッケージなどを使う際にどのように呼び出すことができるかを調べる際にも関数定義の構造をしっかり理解しておくことが役に立ちます。