よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

デコレータの作成と使用

Pythonデコレータと呼ばれる機能がついています。関数がデコレータによって盛られていく様を追います。



デコレータとは、「def」で関数を定義している文の上に「@decorate」とか書かれているヤツのことです。
↓ こんなの

@deco   # ←こやつ
def greet():
    return "Hello World !!"


Webアプリ作成ライブラリのBottle、Flask、Django等の例文はこんなのがたくさんついたものばかりで、何をしているのか全然分からんのですよね。

関数定義の上にデコレータ「@なんとか」を付けると、その関数に「@なんとか」で作っておいた別の機能を追加することができるようになる。
「なんとか」部分はデコレート関数の関数名が入ります。自由に決められます。

では、超かんたんな所から説明してみます。



1. 関数を定義する


基本となる関数をとりあえず作ります。

def greet():
    return "Hello World !!"

これを実行してもreturnで返してるだけなので何も起こりません。表示出来るようにします。

2. 実行部分を追加


def greet():
    return "Hello World !!"

# 実行  # ↓ 追加
print(greet())
Hello World !!

実行すれば上記のように表示されました。

3. 別の関数で呼び出す


上記の「greet()」関数を別の関数で呼び出すようにしてみます。

def greet():
    return "Hello World !!"

def main():  #←関数化した
    print(greet())
    
# 実行
main()  # ←実行する関数を変更した
Hello World !!

結果は同じです。main()の中にgreet()を書き、main()を実行しています。

4. 関数の書き位置を上下で入れ換えてみる


上記の「greet()」と「main()」上下を入れ換えても同じように動きます。
↓ 上下を入れ換えてみる

def main():
    print(greet())

# ↑ 上下を入れ換えた ↓ 

def greet():
    return "Hello World !!"

  
# 実行
main()
Hello World !!

入れ換えても問題ない。

5. 表示を変えたい


表示が味気ないので枠で囲んで色気を出したい。
↓ こんな感じで

--------------
Hello World !!
--------------

このように表示させるには例えば次のように書けばできる。

def main():
    print("--------------") #←追加
    print(greet())
    print("--------------") #←追加

def greet():
    return "Hello World !!"

# 実行
main()
--------------
Hello World !!
--------------

文の文字数にあわせて「-」の個数を数え、上下にprint("--------------")を書き加えた。

6. いろんな関数を呼び出せるようにする


6.1 デコレート関数定義

ここから少々混乱してしまうポイントかと思います。

まず、新たに別の関数decorate()を作ります。関数名は何でも構いません。これがデコレート機能をもつ関数になります。
そしてその中にmain()を中に取り込みます。

def decorate(): # ←新たに定義
    
    def main(): # ←中にそのまま入れた
        print("--------------")
        print(greet())
        print("--------------")
    
    return main  # ←中の関数を返す

def greet():
    return "Hello World !!"

# 実行
main()  
--------------
Hello World !!
--------------

実行すると結果は同じ。



6.2 実行部分を書き替え

次に、最下行の実行部分をgreet()に書き替えます。

def decorate(): 
    
    def main(): 
        print("--------------")
        print(greet())
        print("--------------")
    
    return main  


def greet():
    return "Hello World !!"


# 実行
greet()  #← main()をgreet()に書き替えた
'Hello World !!'

実行すると、なぜか表示されました。一番最初にやった時は表示されませんでしたけど。
一旦無視します。



6.3 デコレート関数を引数に

そして次に、decorate()関数の引数を変数funcに、main()の中のgreet()を変数funcに置き換えます。

def decorate(func): #← 引数追加
    
    def main(): 
        print("--------------")
        print(func()) #←引数追加
        print("--------------")
    
    return main  


def greet():
    return "Hello World !!"


# 実行
greet()
'Hello World !!'

実行すると、なぜか表示されます。
一旦無視します。



7. デコる


ようやく、真打ち「@なんとか」の登場です。
元の関数定義の上にデコレート関数をデコレートする。

def decorate(func):
    
    def main(): 
        print("--------------")
        print(func())
        print("--------------")
    
    return main  

@decorate  # ←デコる
def greet():
    return "Hello World !!"


# 実行
greet()
--------------
Hello World !!
--------------

おっと! 早くも求める表示がされた!!完成です!!



「@decorate」の行以下は、「# 実行」部分を、実行する関数自体を呼び出すように書き替えた以外は触っていない。
デコレータを付けることによって表示を変えることができた。



9. 元の関数とデコレート関数を改造する


Hello World !!」を表示する関数を足し算する関数に変更してみます。

def decorate(func):
    
    def main(a, b): #←引数追加
        print("--計算結果--")
        print(func(a, b)) #←引数追加
        print("-----------")
    
    return main  

@decorate
def plus(a, b): # ←↓ 関数名と内容を変更 
    return a + b


# 実行
plus(5, 2) #← 呼び出す関数名を変更し、引数を渡す
--計算結果--
7
-----------



10. 引数を可変長に変えるetc


引数を辞書や可変長に対応できるようにする。

def decorate(func):
    
    def main(*args, **kwargs): #←変更
        print("--計算結果--")
        print("引数 :", *args,**kwargs)#←変更
        print("答え :", func(*args, **kwargs))#←変更
    
    return main  

@decorate
def plus(*x): #←↓変更
    return sum(x)


# 関数を新規に追加
@decorate
def str_plus(*x):
    x = list(map(str, x))
    return "".join(x)



# 実行
plus(5,6,7)
plus(1,2,3,4,5)

str_plus(5,6,7)
str_plus(11,22,33)
--計算結果--
引数 : 5 6 7
答え : 18
--計算結果--
引数 : 1 2 3 4 5
答え : 15
--計算結果--
引数 : 5 6 7
答え : 567
--計算結果--
引数 : 11 22 33
答え : 112233



おわりに


いまいちデコレータの使い勝手や使うポイントがわかっていないのですが、 オブジェクト指向のクラスのところで@classmethodなど、Webアプリで、コマンドライン引数が簡単に設定できるというライブラリなどで、デコレータがちょくちょく登場するので見直ししました。



以上です。