ホームページ  >  記事  >  バックエンド開発  >  Python デコレータの実行順序に関する神話

Python デコレータの実行順序に関する神話

高洛峰
高洛峰オリジナル
2017-02-17 11:02:441074ブラウズ

複数のデコレーターの実行順序を調べる

デコレーターは、関数やコードをカプセル化するために Python で使用されるツールです。ここで説明したいのは、複数のデコレーターの実行順序に関する迷信です。装飾者。

質問

次の例のように、複数のデコレータを含む関数呼び出しシーケンスのほとんどは、それらがトップダウンであることを示します:

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

def decorator_b(func):
    print 'Get in decorator_b'
    def inner_b(*args, **kwargs):
        print 'Get in inner_b'
        return func(*args, **kwargs)
    return inner_b

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

f(1)

上記のコードは、最初に 2 つの関数を定義します: decotator_a、decotator_b、this によって実装される関数2 つの関数は、関数をパラメータとして受け取り、次に作成された別の関数を返し、この作成された関数内で受け取った関数を呼び出すことです (テキストはコードよりも混乱します)。最後に定義した関数 f は、装飾関数として上記で定義した decotator_a、decotator_b を使用します。パラメータとして 1 を指定して装飾関数 f を呼び出した後、decotator_a、decotator_b の順序はどうなるでしょうか (ここでは、関数の実行順序を示すために、関数の実行順序を表示するために print 出力が使用されます)。

何も考えずにボトムアップ原理で判断すると、decorator_aを先に実行し、decorator_bを実行すると、Get in decotator_a、Get in inner_aが先に出力され、次にGet in decotator_b、Get in inner_bが出力されます。しかし、そうではありません。

実際の実行結果は以下の通りです:

Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f

関数と関数呼び出しの違い

なぜinner_bが最初に実行され、次にinner_aが実行されるのでしょうか?上記の問題を完全に理解するには、まず関数と関数呼び出しという 2 つの概念を区別する必要があります。上記の例では、f は関数と呼ばれ、f(1) は関数呼び出しと呼ばれます。後者は、前者によって渡されたパラメーターを評価した結果です。 Python では、関数もオブジェクトであるため、 f は関数オブジェクトを指し、その値は関数そのものです。 f(1) は関数の呼び出しであり、その値は呼び出しの結果です。ここでの定義は、f(1) 値は 2 です。同様に、上記のdecorator_a関数を例にとると、内部で定義された関数オブジェクトinner_aを返します。 inner_a で関数 func が呼び出され、func の呼び出し結果が値として返されます。

デコレーター関数は、デコレーター関数が定義された直後に実行されます

明確にする必要がある 2 番目の質問は、デコレーターが関数をデコレートするときに正確に何が起こるかということです。次のように仮定して、例を単純化します:

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

デコレータを紹介する多くの記事で述べたように:

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

# 相当于
def f(x):
    print 'Get in f'
    return x * 2

f = decorator_a(f)

したがって、インタプリタがこのコードを実行すると、decorator_a が呼び出され、関数 f をパラメータとして受け取り、関数を返します。内部で生成されるため、その後、f はdecorator_a で返された inner_a を参照します。したがって、今後 f が呼び出されるとき、f に渡されたパラメータは inner_a に渡され、inner_a の関数、つまり f に渡されます。 Final return は f value によって呼び出される関数なので、外見上は f を直接呼び出しているように見えます。

質問の説明

上記の 2 つの概念を明確にすると、最も独創的な例で何が起こったのかが明確にわかります。
インタープリターが次のコードを実行すると、実際には下から上の順にdecorator_aとdecorator_bが呼び出され、対応するGet indecorator_aとGet indecorator_bが出力されます。 このときfはdecorator_bのinner_bに相当します。ただし、f が呼び出されないため、inner_b は呼び出されず、同様に、inner_b 内の inner_a も呼び出されないため、Get in inner_a および Get in inner_b は出力されません。

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

そして、最後の行で、パラメーター 1 を指定して f を呼び出すと、inner_b が呼び出され、最初に inner_b で Get を出力し、次に inner_b で inner_a を呼び出します。つまり、inner_a で Get を出力し、次に inner_a で出力します。元の f が呼び出され、その結果が最終結果として返されます。この時点で、出力がそのようになる理由を理解し、デコレータの実行シーケンスで実際に何が起こるかをある程度理解している必要があります。

上記の例の最後の行にある f の呼び出しを削除し、デモンストレーション用に repl に入れると、順序の問題も自然に確認できます:

➜  test git:(master) ✗ python
Python 2.7.11 (default, Jan 22 2016, 08:29:18)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test13
Get in decorator_a
Get in decorator_b
>>> test13.f(1)
Get in inner_b
Get in inner_a
Get in f
2
>>> test13.f(2)
Get in inner_b
Get in inner_a
Get in f
4
>>>

実際のアプリケーション シナリオでは、上記のメソッドを使用するとき最初にログインしているかどうかを確認する @login_required 、次に権限が十分であるかどうかを確認する @permision_allowed など、2 つの A 装飾メソッドを記述するには、次の順序で関数を装飾します:

@login_required
@permision_allowed
def f()
  # Do something
  return

Python デコレーターに関連するその他の記事はこちら実行順序に関する神話については、PHP の中国語 Web サイトに注意してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。