1. ホーム
  2. スクリプト・コラム
  3. パイソン

Pythonの@decoratorsについてまとめてみました。

2022-01-08 08:32:51

プログラミングをしていると、以下のような必要性にしばしば遭遇します。

たとえば、電卓を開発したいのですが、すでにいろいろな計算をするための関数はたくさん書いてあるので 関数を実行する前に、入力データが数値でなければならないことを確認する必要があります。 であり、文字列ではない。

他の例としては、三角形の周囲や面積、ある角度を計算するモジュールを書きたいのですが、すでに計算のための関数はいくつか書いてあるので 計算を行う前に、まず、入力された3つの辺の長さが三角形を形成できることを確認します。 の前に、計算を行うことに意味があるのです。

では、例えば、あるWebアプリケーションを開発し、あるユーザーのアクションを実装するための関数を書きたい場合、そのために は、これらのアクションの実行を許可する前に、ユーザーがログインしていることを確認する必要があります。 .

これらの要件を煮詰めていくと、次のようになります。 強 多くの場合、メイン関数の前に何らかのプリファンクションを実行しなければなりません。 メイン関数の前に、チェックサムか何かを行うためです。

この種の要件は非常に一般的であり、プログラムの完全性と堅牢性を確保するための重要なステップです。では、どうすればもっと簡単にできるのでしょうか?

と言われるかもしれませんが、それは簡単です。各関数の中にif文を書けばいいだけだ。その電卓の場合、足し算、引き算、掛け算、割り算を書くとすれば、次のようになる。

def plus(a,b):
    if type(a)==type(0) and type(b)==type(0): #Assume the calculator can only calculate integers, if you want to calculate decimals then or type(0.0)
        return a+b
    else:
        print('Type must be number') #If the data type is not correct, the alarm is output first and the function returns None
        return None

def minus(a,b):
    if type(a)==type(0) and type(b)==type(0):
        return a-b
    else:
        print('Type must be number')
        return None

def multiply(a,b):
    if type(a)==type(0) and type(b)==type(0):
        return a*b
    else:
        print('Type must be number')
        return None

def divide(a,b):
    if type(a)==type(0) and type(b)==type(0):
        return a/b
    else:
        print('Type must be number')
        return None

まあ、これは素直で暴力的なんだけどね。でもって、ここには4つの機能しかないんです。 もし、何十、何百もの関数がある電卓を開発していて、その一つ一つにif文を入れていたら、大変な手間と口うるささになってしまうでしょう これは大変な作業ですね。

では、どうすれば簡単になるのか?巧妙に考えたに違いないが、その判定ifも別の関数を定義して、その中に計算のための関数をこんな風に入れればいいのだ。

{{コード
{未定義

ここで特に注意しなければならないことがあります。メインプログラムのcheck(1,2,plus)は、plus関数自体を変数としてチェックに渡し、plus関数の実行方法をチェック関数が決定します。ここではcheck(1,2,plus(1,2))とは書けません。plusにはパラメータや括弧がとれず、 plus() と実行して その結果を checkに渡しているのです。

このようにプログラムを書くと、かなりすっきりします。 足し算、引き算、掛け算、割り算の各関数はそれぞれ演算を定義すればよく、変数の検出はcheck関数に任せます 変数のチェックはチェック関数/strongに任されている。これもわかりやすいですね。

しかし、プログラムを使うユーザーにとっては、それほど 非常に読みにくいと感じるだろう .

なぜ?をするためにプログラムを受講しているんです。 足し算、引き算、掛け算、割り算 の計算をするのですが、何を計算しても メインで毎回関数チェックを呼び出しています。 !

では、見た目の美しさと簡潔さを同時に実現する方法はないのでしょうか。デコレーターはこの魔法を使います。

上記の要件は、デコレータを使うと次のように書くことができます。

def check(a,b,func): #Define the check function with variables a,b and func to be executed when the check is passed
    if type(a)==type(0) and type(b)==type(0):
        return func(a,b)
    else:
        print('Type must be number')
        return None

def plus(a,b):
    return a+b

def minus(a,b):
    return a-b
...

#Main program
check(1,2,plus) #calculate 1+2
check(1,2,minus) #compute 1-2
check(1,2,multiply) #Calculate 1*2
check(1,2,divide) #Calculate 1/2

を可視化するところから始めてみましょう。 check 関数は @check を介して plus 関数に「注入」されます。 で、プラス関数にパラメータをチェックする機能を持たせています。したがって、メインプログラムでは、足し算の計算をしたい場合、直接プラスを呼び出して、計算前にチェックすることができるのです。

では、このデコレータ・チェックはどのように定義するのでしょうか。見てみましょう。

def check(func):
    ...

@check
def plus(a,b):
    return a+b

@check
def minus(a,b):
    return a-b

...

#Main program
plus(1,2) #calculate 1+2
minus(1,2) #compute 1-2
...

ということがわかります。 デコレータ @check がプラス関数に作用するとき、プラス関数そのものがパラメータ func としてデコレータに渡されます。デコレータ check の定義の内部では、関数テンプレートが定義されており、 入力 func に対してどのような処理を行うかが記述されています。 . ご覧の通り、newfunc は func(つまり入力プラス)に対して、データ型を決定する if 文を適用しています。 最後に、newfuncは元のfuncを置き換えるように出力されます。 . このように、funcを実行することはnewfuncを実行することであり、plusを実行することはif文による新しい関数を実行することである。

そこで 判定文を持つ新しい関数は、プラス関数をデコレーターで置き換えますが、プラス関数の名前で呼び出されます ということで、プラス関数が「装飾」されたように見えるのです。

もちろん、デコレーターの定義方法をウェブで検索すれば、より標準化されたバージョンを見ることができる。少し難しく見えるが、実は同じことなのだ。

def check(func): #define the decorator check
    def newfunc(a,b): #define function template, i.e. how to handle func
        if type(a)==type(0) and type(b)==type(0):
            return func(a,b)
        else:
            print('Type must be number!')
            return None
    return newfunc # output the processed func as a new function newfunc

@check
def plus(a,b):
    return a+b

#Main program, calculate 1+2
plus(1,2)

テンプレート機能は、一般的にラッパーを使用して表現するために使用され、これは何も、我々はすべてこのように記述することをお勧めします、いくつかの標準化。

引数は一般に不定長の*args,**kwargsで表され、人によっては混乱するかもしれません。なぜなら、装飾された関数は何種類もあるかもしれないし、引数の数は一般に不定だからである。では、*args,**kwargsは何かというと、args,kwargsは参照の英字の2つの形式は、あなたが判断できることは重要ではありません、キーはシングルアスタリスク*とダブルアスタリスク**の前にある。

関数を定義すると、引数の数はわからないが、例えば、入力に対して連続した足し算の演算をするための数値の集合を行う。すると、plus(*x)を定義することができ、この関数が呼ばれたとき、複数の変数plus(1,2,3)が入力されていれば、入力変数は一つのメタ祖x=(1,2,3)の入力に結合されるのです。ダブルアスタリスクplus(**x)を定義すると、フォーム変数plus(a=1,b=2,c=3)が書き出された状態で関数が呼ばれると、入力変数は辞書 x={a:1,b:2,c:3} に結合されて関数に引き渡されます。

もちろん、あなたも操作を逆にすることができます、関数を定義するときにパラメータの数が明示的にプラス(a、b、c)は、その後、関数を呼び出すと、アスタリスクプラス(*(1,2,3))は、プラス(1,2,3)入力に変換、吹き上げ操作を行うには入力先祖(1,2,3)されています。

これをデコレーターに書く意味は何でしょうか?さっきの newfunc(a,b) をよく見てみよう。つまり、新しい関数の引数が a,b の2つであることを指定しているが、デコレートされる元の関数が3つの引数を持つ場合はどうだろう?使い物にならなくないですか?

wrapper(*args,**kwargs)で定義された、つまり、引数の数に関係なく、ラッパーにパッケージされています。ラッパーで、元の関数とfunc(*args,**kwargs)、つまり、入力メタ祖先は解凍されてからfuncに渡されますを呼ぶときに見てみましょう。 このパッキングとアンパッキングは、一見何もしていないように見えますが、関数の引数の不確実性に対応しており、デコレータは引数の数が異なる複数の関数を装飾することができます。 .

この辺にしておきましょう。

Pythonの@decoratorは何のためにあるのか、この記事はこれで終わりです。Pythonの@decoratorについては、過去の記事を検索するか、引き続き以下の記事を閲覧してください。