3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

pythonのデコレータ

Last updated at Posted at 2023-09-04

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

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?