関数

【Python】関数定義の基本

【Python】関数定義の基本

Pythonで関数を定義する方法について解説します。関数定義では、色々な引数の種類や使い方が登場します。それらの使い方を含めて詳細を説明していきます。

Pythonにおける関数

関数とは、特定の処理を実行するためのコードのまとまりのことです。適切な関数定義を行うことで、再利用可能なコードブロックとして様々な場所から必要な時に呼び出して処理を実行することができるようになります。

この記事では、Pythonにおける関数定義の基本を紹介します。また、関数では引数の扱いが重要です。様々な引数の種類や使い方についても詳細に説明します。

基本的な関数宣言

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

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


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

上記例では、"hello world!"と表示する関数print_hello_worldを定義しています。関数を呼び出す際には、print_hello_world()というように呼び出すことが可能です。

Note

if __name__ == "__main__": の意味について

この構文は、Pythonのエントリーポイントを示すために使用されます。具体的には、スクリプトが直接実行された場合と、他モジュールからインポートされた場合で異なる動作をさせることができます。

状況動作概要
スクリプトが直接実行されたとき__name__"__main__"となるため、ifブロック内のコードが実行されます。
他モジュールからインポートされたとき__name__はモジュール名になり、ifブロック内のコードは実行されません。

if __name__ == "__main__":のブロックがない場合、スクリプトをインポートすると、スクリプト内のコードが上から順に実行されてしまいます。これは、ライブラリとして使う場合には意図しない動作を引き起こす可能性があります。しかし、以下のようなブロックを追加することで、他モジュールからインポートされた際に不必要なコードの実行を防ぐことができます。

if __name__ == "__main__":
    # 直接実行したときにのみ実行される
    function()

または、何も実行しない場合は以下のような記載も適切です。

if __name__ == "__main__":
    pass

このようにスクリプトの直接実行とインポート時の動作を分けることは、Pythonでのベストプラクティスの一つです。

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

関数は、引数を受け取り処理を実行し、結果を返り値として呼び出し元へ返却します。引数は関数の()内に記載をし、返り値についてはreturnを使用して返却します。

# 関数定義 引数、返却値
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を使用します。変数を指定するか、式を直接指定した場合は式が評価された結果が返却されます。返り値がない場合は、returnを省略した関数とするか、return Noneを用いることが一般的です。

関数の呼び出し時には、y1 = func_2(x1)のように呼び出しができます。実引数x1の値が関数の仮引数numに渡され処理が実行されます。もちろんdef func_values(x1, x2):のように複数の引数を受け取る関数を定義することも可能です。

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

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

関数の引数にはいくつかの種類や考え方があります。以降では「位置引数」「キーワード引数」「引数のデフォルト値」「*args」「**kwargs」といったトピックスについて詳細を説明していきます。

位置引数

位置引数(positional arguments)とは、関数を呼び出す際に引数の順番に基づいて値を渡す方法です。位置引数では、関数定義時に指定されている引数の順番に値を渡す必要があります。

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'}

上記例は、入力された名前(first_name)、苗字(last_name)、誕生日(birthday)を受け取って辞書にして返却するmake_profile関数です。

位置引数を使用する場合には、引数の位置と順番が非常に重要です。例えば、上記例で2回目に呼び出した際には、fist_namelast_nameの変数の指定位置が入れ替わっています。結果として、名前であるTaroが苗字として処理されてしまっています。

位置引数では、呼び出し時の順序が重要です。呼び出し順序を間違えると予期しない動作をする可能性がありますので十分に注意しましょう。

キーワード引数

キーワード引数(keyword arguments)とは、関数を呼び出す際に引数名を指定して値を渡す方法です。関数定義で指定された引数名に基づいて値を渡すため、順番に依存せずに引数を指定できることが特徴です。

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'}

上記例は、位置引数の説明時と同様の関数です。キーワード引数の呼び出しでは、「仮引数名=」という形で指定します。

キーワード引数では呼び出し時に仮引数名を指定するため順番は関係がなくなります。上記例でいうと、make_profile(birth=birthday, last=last_name, first=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'}

引数のデフォルト値は「仮引数=デフォルト値」という形で定義します。呼び出し元で引数が指定された場合は、その値が使用されますが、指定がない場合はデフォルト値が使用されます。

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

*argsによる任意個の位置引数の受け取り

関数の引数として任意の個数の引数を渡したい場合には、関数の引数として*argsを定義します。なお、argsという名称の使用は必須ではありませんが、Pythonを使っている人は通例としてargumentsを意味するargsという名称を使用します。

基本的な使い方

*argsによる引数の受け取りは以下のようにします。

# *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として引数を設定した場合には、受け取った引数はタプルとなります。上記例では、print_input(1, 2, "A", "B", [1, 2])のように合計5つの引数を受け取ることができています。

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

*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

上記例では、input1input2は位置引数として指定された順で値が設定され、残りの引数がすべてargsにまとめられていることが分かります。これにより、関数として重要な引数は、位置引数として定義しておき、任意の引数をargsにまとめてしまうといったことができます。

なお、通常の引数は*argsの前に定義する必要があるため注意してください。

**kwargsによる任意個のキーワード引数の受け取り

関数の引数として任意の個数のキーワード引数を渡したい場合には、関数の引数として**kwargsを定義します。なお、kwargsという名称は必須ではありませんが、Pythonを使っている人は通例としてkeyword argumentsを意味するkwargsという名称を使用します。

基本的な使い方

**kwargsによるキーワード引数の受け取りは以下のようにします。

# **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として引数を指定した場合には、受け取った引数は辞書となります。上記例では、合計3つのキーワード引数を受け取ることができています。**kwargsの使用は、特に複数のオプションとなる引数を関数に渡す場合などに非常に便利です。

通常の引数や*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

上記の例では、通常の位置引数であるin1, in2、任意個の引数の*args、任意個のキーワード引数の**kwargsprint_input関数の引数にしています。このように組み合わせをすることで、非常に柔軟に関数の挙動を定義することが可能になってきます。

ただし、引数の順は、通常の引数、*args**kwargsの順で定義する必要があるため注意してください。

関数の引数に関する注意事項

ここでは関数の引数に関する注意事項を説明します。

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

関数の引数のデフォルト値にはミュータブル(mutable)な値を使うべきではありません

デフォルト値にミュータブルな型を使用すると、同じインスタンスが再利用されてしまい、意図しない動作を引き起こすことがあります。以下の例で見てみましょう。

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']

上記例のsample_function関数は、値とリストを受け取ってリストに値を追加する関数です。デフォルト値として空リスト[]を設定しています。この時、前提としてプログラムを書いた人はリストが指定されていないときは新しくリストを作成することを想定しているとします。

関数の呼び出し結果を見てみましょう。2回呼び出しを行っており、いずれもtmp_listに渡すリストは指定していません。そのため、結果は1回目は['A']、2回目は['B']となってほしいわけです。しかし、2回目の呼び出しでは結果が['A', 'B']となってしまいました。

このような結果となるのは、Pythonでは関数のデフォルト値は最初に1回だけ評価されるためです。2回目以降の呼び出しでは既に'A'が追加されているリストを使ってしまうため、['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']

上記結果では、想定した通り結果では、呼び出しの1回目は['A']、2回目は['B']という結果となりました。

まとめ

Pythonで関数を定義する方法について解説しました。また、関数定義では、色々な引数の種類や使い方について紹介しています。

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

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

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