pythonのデコレータについて調べてみました。すぐに内容を忘れてしまうので備忘録として書いておきます
デコレータの分類
デコレータには2つの関数が使われます。一般的に書くと以下のようになります。実引数は、具体的な値が入ります。すなわち、関数を呼び出す場合と考えればよいです。仮引数は、関数の内容を定義される際に使われるパラメータの意味です。
@デコレータ関数(実引数)
def 参照関数(仮引数)
ここで、それぞれの関数は引数を取ることができますが、引数の有無により4種類のパターンが考えられます。
- パターン1
@デコレータ関数()
def 参照関数()
- パターン2
@デコレータ関数()
def 参照関数(仮引数)
- パターン3
@デコレータ関数(実引数)
def 参照関数()
- パターン4
@デコレータ関数(実引数)
def 参照関数(仮引数)
ここでは形式的に引数の有無で4パターンに分類しましたが、 実際にはパターン1,および2 のデコレータ関数に()を追加するとエラーが発生するので、()を省略して書く必要があるようです。すなわち以下のようになります。
- パターン1(改)
@デコレータ関数
def 参照関数()
- パターン2(改)
@デコレータ関数
def 参照関数(仮引数)
これらの4パターンについて具体的に関数の記述例を示します。
パターン1
- デコレータを使った関数の記述
def decorator(func):
def wrapper():
print('--start--')
func()
print('--end--')
return wrapper
@decorator
def test():
print("test was called")
test()
- 実行結果
--start--
test was called
--end--
なお、デコレータは既存の関数を再定義するためのシンタックスシュガーであり、上記のプログラムは以下と同等になります。
- デコレータを使わない関数の記述
def decorator(func):
def wrapper():
print('--start--')
func()
print('--end--')
return wrapper
def test():
print("test was called")
test = (decorator)(test) # 再定義された関数
test() # 関数の実行
- 実行結果
--start--
test was called
--end--
パターン2
- デコレータを使った関数の記述
def decorator(func):
def wrapper(*args,**kwargs):
print('--start--')
func(*args,**kwargs)
print('--end--')
return wrapper
@decorator
def test(*args,**kwargs):
no = args[0]
name = kwargs["name"]
print('no=',no)
print('name=',name)
test(3,name="tokyo")
- 実行結果
--start--
no= 3
name= tokyo
--end--
この例の場合、デコレータを使わない関数の実行は以下のようになります
(decorator)(test)(3,name="tokyo")
パターン3
- デコレータを使った関数の記述
def decorator(*args,**kwargs):
no = args[0]
name = kwargs['country']
def wrapper(func):
print('no=',no)
print('country=',name)
print('--start--')
func()
print('--end--')
return func
return wrapper
no=3
name='japan'
@decorator(no,country=name)
def test():
print("test was called")
- 実行結果
no= 3
country= japan
--start--
test was called
--end--
この例の場合、デコレータを使わない関数の実行は以下のようになります
(decorator(3,country='japan'))(test)
パターン4
このパターンではデコレータ関数内で入れ子の関数定義を1段増やします。
- デコレータを使った関数の記述
def decodecorator(*para,**kwpara):
no = para[0]
name = kwpara['city']
def decorator(func):
def wrapper(*args,**kwargs):
print('no=',no)
print('city=',name)
print('--start--')
func(*args,**kwargs)
print('--end--')
return
return wrapper
return decorator
no=3
name="tokyo"
@decodecorator(no,city=name)
def test(*para,**kwpara):
numbr = para[0]
label = kwpara['food']
print('numbr=',numbr)
print('food=',label)
num=7
eatable="sushi"
test(num,food=eatable)
- 実行結果
no= 3
city= tokyo
--start--
numbr= 7
food= sushi
--end--
この例の場合、デコレータを使わない関数の実行は以下のようになります
no=3
name="tokyo"
num=7
eatable="sushi"
(decodecorator(no,city=name))(test)(num,food=eatable)
デコレータ関数の入れ子呼出
上記のパターン4をさらに発展させた例として、デコレータを入れ子にして呼び出す例を示します。この関数は、初期値$n$を与えて、$B*(n+A)$を求めます。足し算をdeco_add、掛け算をdeco_multiplyで行っています。この例では足し算$\rightarrow$掛け算の順に実行されますが、deco_addとdeco_multiplyの順番を逆にすると掛け算$\rightarrow$足し算の順となり $B+(n*A)$が計算されます。
def deco_add(para):
print("para_a=",para)
def add(func):
def wrapper(*args,**kwargs):
val = para+func(*args,**kwargs)
return val
return wrapper
return add
def deco_multiply(para):
print("para_m=",para)
def multiply(func):
def wrapper(*args,**kwargs):
val = para*func(*args,**kwargs)
return val
return wrapper
return multiply
@deco_multiply(3)
@deco_add(5)
def init(para):
num = para
return num
n=1
print(init(n))
- 実行結果
para_m= 3
para_a= 5
18 注: 3*(1+5)が計算されている
まとめ
pythonのデコレータについて記述方法を調べました。誰かの役に立てれば幸いです。
参考資料:https://www.geeksforgeeks.org/decorators-with-parameters-in-python