関数 応用#
再帰関数#
コールバック関数#
- 参考: 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 関数内関数 )
上記のコードを説明すると、
outer(4,7)
で 外側の関数へ 実引数 4 と 7 を渡して呼び出します。- outer関数が呼ばれた後、return 文で inner 関数が呼び出さます。
- このとき inner 関数へ outer 関数で渡した実引数が渡されます。
- inter 関数内の処理が行われ、その結果を返します。
- 従って、戻り値は 11 となります。
高階関数#
Python では関数自体もオブジェクトです。引数に関数を渡すことも、また戻り値として関数を返すこともできます。 それを応用したのが高階関数です。
クロージャ#
クロージャとは他の関数によって動的に生成される関数で、自分の外で作られた変数の値を変えられたり、覚えたりすることができる。(入門Python3 第2版 - 9.6.1 クロージャ )
構文は以下の通りです。
クロージャー変数 = 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
文を用います。
デコレータ#
デコレータは、元の関数を変更せずに関数の動作を拡張する構文です。 デコレータを理解するためには以下の内容を理解していることが前提です。
- ネストされた関数(関数のスコープ)
- 高階関数(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関数を定義し、元の関数に"サヨナラ~"を言うように機能を追加します。
後から装飾した箇所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) なしの動作
-
@wraps(func) を追加した時の動作