Skip to content

関数 応用#

再帰関数#

コールバック関数#

  • 参考: DelftStack - Python のコールバック関数
  • コールバック関数とは、"将来のある時点で実行されるために引数として渡される関数"(用語集 - callback)のこと

  • 言い換えると、予め後で呼び出される(コールバック)関数を定義しておき、 呼び出し側の関数にて、引数で指定し実行される関数のこと。

# callback 関数の定義
def callback_func(word):
    print("CallBack_func : ", word)

# 呼び出し側の関数では、引数に関数オブジェクト(callback 関数)を指定します。
def display_word(word, callback):
    callback(word)

if __name__ == '__main__':
    display_word("Hello world !", callback_func)

関数内関数#

関数内関数は、関数内で複数回実行される複雑な処理を実行したいときに役立つ。ループやコードの重複を避けられるのである。(入門Python3 第2版 - 9.6 関数内関数 )

def outer(a, b):
    def inner(c,b):
        return c + b
    return inner(a,b)

outer(4,7)
# 11

上記のコードを説明すると、

  1. outer(4,7) で 外側の関数へ 実引数 4 と 7 を渡して呼び出します。
  2. outer関数が呼ばれた後、return 文で inner 関数が呼び出さます。
  3. このとき inner 関数へ outer 関数で渡した実引数が渡されます。
  4. inter 関数内の処理が行われ、その結果を返します。
  5. 従って、戻り値は 11 となります。

高階関数#

Python では関数自体もオブジェクトです。引数に関数を渡すことも、また戻り値として関数を返すこともできます。 それを応用したのが高階関数です。

クロージャ#

クロージャとは他の関数によって動的に生成される関数で、自分の外で作られた変数の値を変えられたり、覚えたりすることができる。(入門Python3 第2版 - 9.6.1 クロージャ )

構文は以下の通りです。

def outer関数(仮引数)
    # 内側の関数 → クロージャー
    def inner関数():
        仮引数 を用いた処理
    return inner関数

クロージャー変数 = outer関数(実引数) のような形でクロージャを呼び出します。 このクロージャー変数 は関数オブジェクトです。クロージャー変数に()を付けて呼び出すと クロージャ変数宣言時に outer関数 に渡した実引数を保持していることが分かります。

以下、実際にコードで確認してみます。

def circular_constant(pi):
  def calc_circleArea(radius):
    return radius * radius * pi

  return calc_circleArea # 内側の関数を()を付けずに返す

# 異なる実引数を保持した内側の関数を定義
pattern1 = circular_constant(3.14)
pattern2 = circular_constant(3.141)

print(type(pattern1)) # <class 'function'>
print(type(pattern2)) # <class 'function'>

# radius は同一だがクロージャ呼び出し時の実引数により結果は異なる。
print(pattern1(10))  # 314.0
print(pattern2(10))  # 314.1

クロージャの特徴を以下にまとめます。

  • 関数の中に関数を定義します。
  • 外側の関数は内側の関数自体を返します。(括弧はつけない)
  • クロージャ(外側の関数)呼び出し時に渡す実引数を、内側の関数内で保持することができます。

Info

外側の関数で定義した変数を使用する場合は nolocal文を用います。

def outer_function(outer_arg):
    num = outer_arg

    def increment_num():
        nonlocal num
        num += 5
        return num
    return increment_num

item1 = outer_function(10)
print(item1()) # 15
print(item1()) # 20

デコレータ#

デコレータは、元の関数を変更せずに関数の動作を拡張する構文です。 デコレータを理解するためには以下の内容を理解していることが前提です。

  • ネストされた関数(関数のスコープ)
  • 高階関数(Python は関数自体もオブジェクトであり関数を引数・戻り値として利用できる)
  • クロージャ(外側の関数呼び出し時の実引数を内部関数で保持できる)

以下はデコレータの例です。

def decorator(func):
    def basic_greet():
        print("オハヨウゴザイマス")
        func() # 後で装飾する箇所
        print("コンバンハ")
    return basic_greet

# 追加したい機能を関数で定義します
def say_hello():
    print("コンニチハ !!")

# 外側の関数を呼び出し、追加で定義した関数自体を引数として渡し、関数を装飾します。
say_hello = decorator(say_hello)
say_hello # <function decorator.<locals>.basic_greeting at 0x0000022D3A90F8B0>
say_hello() 

# オハヨウゴザイマス
# コンニチハ !!
# コンバンハ

今度はsay_bye関数を定義し、元の関数に"サヨナラ~"を言うように機能を追加します。

def say_bye():
    print("サヨナラ~")

say_bye = decorator(say_bye)
say_bye()
# オハヨウゴザイマス
# サヨナラ~
# コンバンハ

後から装飾した箇所say_hello = decorator(say_hello) say_bye = decorator(say_bye) は 糖衣構文(シンタックスシュガー)にて書き換える事ができます。糖衣構文は@クロージャ関数の構文で 記述します。

@decorator
def say_wait():
    print("チョットマッテ、オニイサン。")

# いつも通り関数を呼び出すだけで装飾できていることが確認できる。
say_wait()

# オハヨウゴザイマス
# チョットマッテ、オニイサン。
# コンバンハ

補足:functools.wraps#

ラッパー関数を定義するときには@wrapsをつけて、デコった関数の関数名、docstring が失われないように。

from functools import wraps

def sample_decorator(func):
    @wraps(func)
    def wrapper():
        """説明:これはデコレータ関数です。"""
        print('これはデコレータ関数です。')
        return func()
    return wrapper

@sample_decorator
def expand_function():
    """説明:これは追加した関数です。"""
    print('これは追加した関数です。')

if __name__ == '__main__':
    expand_function()
    print(expand_function.__name__)
    print(expand_function.__doc__)
  • @wraps(func) なしの動作

    # これはデコレータ関数です。
    # これは追加した関数です。
    # wrapper
    # 説明:これはデコレータ関数です。
    

  • @wraps(func) を追加した時の動作

    # これはデコレータ関数です。
    # これは追加した関数です。
    # expand_function
    # 説明:これは追加した関数です。