Python デコレータ入門

はじめに

今回は初学者がつまずきやすいとされている、Pythonのデコレータについてまとめます。

デコレータとは

デコレータとは、あるオブジェクトに対し、その構造を変更せずに新しい機能を追加するようなデザインパターンと言えるでしょう。

自分でも何を言っているのか、よくわからないので具体例をみていきましょう。

先にソースコード全体を貼り付けておきます。

ソースコード

1
2
3
4
5
6
7
8
9
10
11
12
13
def my_decorator(my_func):
def decorator_func():
print('処理0')
my_func()
print('処理2')
return decorator_func

@my_decorator
def my_func():
print('処理1')

if __name__=='__main__':
my_func()

関数を修飾する

いま、以下のような関数funcが存在するとしましょう。

1
2
def my_func():
print('処理1')

あなたは、この関数が呼び出される前に、処理0を追加したいとします。
その場合、以下のようにするのが良いでしょう。

1
2
3
if __name__=='__main__':
print('処理0')
my_func()

では、関数my_funcがプログラムの中で複数回呼び出され、かつその全ての呼び出し前に処理0を追加したい場合はどうしますか。

全ての関数my_funcの前に処理0を追加するのは面倒ですし、追加漏れが発生してバグの温床にもなりそうです。

であれば、関数my_func自体に処理0を追加してしまえば良いでしょう。

1
2
3
def my_func():
print('処理0')
print('処理1')

これで問題ありません。

しかし、関数my_funcがあなたがプログラムで定義している関数ではなく、ライブラリで定義されている関数の場合はどうでしょうか。

その場合は、関数を編集することはできません。

そういった時は、
関数を引数として受け取り、関数を返却する関数
を考えれば良いのです。

そんな事ができるのかと思れるかもしれませんが、実際にコードをみてみましょう。

1
2
3
4
5
def my_decorator(my_func):
def decorator_func():
print('処理0')
my_func()
return decorator_func

関数my_decoratorは関数my_funcを引数にとり、関数decorator_funcを返却します。

関数my_decoratorを見ればわかるように、処理0の後に関数my_funcを呼び出しています。

これを応用すると、関数my_funcの後に処理を行うこともできます。

1
2
3
4
5
6
def my_decorator(my_func):
def decorator_func():
print('処理0')
my_func()
print('処理2')
return decorator_func

さて、関数my_decoratorを作る事ができたので、関数my_funcを更新してみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def my_decorator(my_func):
def decorator_func():
print('処理0')
my_func()
print('処理2')
return decorator_func

def my_func():
print('処理1')

my_func = my_decorator(my_func)

if __name__=='__main__':
my_func()

実行結果は、

1
2
3
処理0
処理1
処理2

となり上手くいったようです。

シンタックスシュガー

さて、実は以下のコードですが、

1
2
3
4
def my_func():
print('処理1')

my_func = my_decorator(my_func)

以下のコードでも等価となります。

1
2
3
@my_decorator
def my_func():
print('処理1')

若干、記述が簡潔になりました。

これがデコレータとして出てくる@の正体でした。

なお、プログラミングの世界では、ある構文をより簡単に記述できるようにした構文をシンタックスシュガーと呼んだりします。

まとめ

これで、コードが完成しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
def my_decorator(my_func):
def decorator_func():
print('処理0')
my_func()
print('処理2')
return decorator_func

@my_decorator
def my_func():
print('処理1')

if __name__=='__main__':
my_func()

最初に述べたとおり、デコレータとは、あるオブジェクトに対し、その構造を変更せずに新しい機能を追加するようなデザインパターンです。

そのため厳密には、関数に関数を修飾させる処理だけがデコレータではないので、その点には注意してください。

記事情報

  • 投稿日:2020年3月13日
  • 最終更新日:2020年3月13日