0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonのデコレータ関数について

Posted at

デコレータ関数とは?

既存の関数を装飾して、新しい機能を追加する事ができる仕組みです。

デコレーションされる元の関数を変更せずに、機能を拡張できます。

■高階関数とクロージャ

高階関数とは、関数を引数に受け取ったり、関数を戻り値として返す関数のことを言います。

クロージャとは、内部関数(関数の中に定義された別の関数)が外側の関数の変数を参照できる仕組みのことです。

def decorator_fun(func):
    def wrap():
        print('こんにちは')
        func()
        print('さようなら')
    return wrap
    
def introduce():
    print('私の名前は田中です。')
    
introduce = decorator_fun(introduce)
introduce() 

# 出力
こんにちは
私の名前は田中です
さようなら

例えば、introduce関数をdecorator_fun関数の引数として渡します。

そうすると、decorator_fun関数ではwrap関数が返ってきます。

wrap関数内で実行しているfunc()は、decorator_fun関数の引数に渡された値を参照します。

そのため、introduce()で関数を実行すると、「こんにちは 私の名前は田中です。 さようなら」と表示されます。

この高階関数とクロージャの仕組みを利用して、既存の関数を装飾する仕組みがデコレータ関数といいます。

デコレータ関数の基本的な書き方

# デコレーター関数
def decorator_fun(func):
    def wrap():
        print('こんにちは')
        func()
        print('さようなら')
    return wrap

@decorator_fun
def introduce():
    print('私の名前は田中です。')

introduce()

# 出力
こんにちは
私の名前は田中です
さようなら

先程の、高階関数とクロージャのコードをデコレータ関数で書き直してみます。

デコレーター関数は、@デコレータ名を関数の上に書きます。

@decorator_funを書くと、introduce関数の実装を変更せずに、decorator_fun関数でintroduce関数を拡張できます。

具体的な実行順序は、

  1. introduce()を実行すると、裏側でintroduce = decorator_fun(introduce)が実行されます。そのため、introduce()関数はdecorator_fun関数の引数に渡されて、wrap()関数が返ってきます。
  2. 実際に実行されるのはwrap()関数となります。wrap()関数が実行されると、introduce()関数の処理の前後に新しく処理を追加することができます。

■引数と戻り値のあるデコレータ

def decorator_func(func):
    def wrap_func(*args, **kwargs):
        print("wrap_func関数の実行開始")
        result = func(*args, **kwargs)
        print("wrap_func関数の実行終了")
        return result
    return wrap_func

@decorator_func
def add(a, b):
    print(f"{a} + {b} = {a + b}")
    return a + b

# add = measure_time(add)
result = add(5, 10)
print(result)

# 出力
wrap_func関数の実行開始
5 + 10 = 15
wrap_func関数の実行終了
15

次にデコレーター関数に引数と戻り値を書いた場合のサンプルコードになります。

この場合も、add(5, 10)を実行すると、内部的にはadd = measure_time(add)というコードが実行されています。

そのため、add(5, 10)を実行した際に実行されているのはwrap_func関数が実行されています。

wrap_func関数の第1引数は*argsなので、可変長引数になっています。

第2引数の**kwargsはキーワード可変長引数になります。

今回は第1引数の*argsにタプル型で(5, 10)が渡ってきて、第2引数は空のdictionary型が渡ってきています。

add関数側では、(a, b)と引数が指定されているので、タプルの(5, 10)をそれぞれの引数で受け取って実行されます。

def decorator_func(func):
    def wrap_func(*args, **kwargs):
        print("wrap_func関数の実行開始")
        result = func(*args, **kwargs)
        print("wrap_func関数の実行終了")
        return result
    return wrap_func

@decorator_func
def add(a, b):
    print(f"{a} + {b} = {a + b}")
    return a + b

# add = measure_time(add)
result = add(a=5, b=10)
print(result)

# 出力
wrap_func関数の実行開始
5 + 10 = 15
wrap_func関数の実行終了
15

もし第2引数の**kwargsに引数を渡したい場合は、add(a=5, b=10)と指定します。

この場合は、*argsは空のタプル型が渡ってきて、**kwargs{'a': 5, 'b': 10}というデータが渡ってきます。

■引数を持つデコレータ

import random

def sample_function(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = []
            for _ in range(num_times):
                result.append(func(*args, **kwargs))
            return result
        return wrapper
    return decorator

@sample_function(3)
def random_calculation(a, b):
    noise = random.random()
    return (a * b) + noise

results = random_calculation(5, 10)
print(results)

# 出力例
[50.84876650000651, 50.7977302143835, 50.84527666135434]

次は、@sample_function(3)のようにデコレーター関数に引数を渡してみます。

このコードの実行順序は以下になります。

  1. random_calculation(5, 10)を実行すると、内部でdecorator = sample_function(3)が実行されます。なのでdecoratorには、sample_function関数の戻り値であるdecorator関数が代入されます。

  2. 次に、wrapper = decorator(random_calculation)が実行されます。decoratorには、sample_function関数の戻り値であるdecorator関数が代入されていたので、decorator関数が実行されて、引数としてrandom_calculation関数が渡されます。

    その結果、wrapper関数が戻り値として返ってきます。

  3. wrapper(5, 10)が実行されます。そうするとwrapper関数がnum_times回ループして、random_calculation関数を実行します。

このような順序を経て、デコレーター関数が実行されます。

■参考資料

Pythonのデコレータを理解するまで
[Python] 可変長引数と引数の展開 - Qiita
Pythonの可変長引数(*args, **kwargs)の使い方 | note.nkmk.me

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?