デコレーターの実行順序


93
def make_bold(fn):
    return lambda : "<b>" + fn() + "</b>"

def make_italic(fn):
    return lambda : "<i>" + fn() + "</i>"

@make_bold
@make_italic
def hello():
  return "hello world"

helloHTML = hello()

出力: "<b><i>hello world</i></b>"

私はデコレーターについて大まかに理解し、ほとんどの例でそれがどのように機能するかを理解しています。

この例では、2つあります。出力から、@make_italic最初に実行され、次にが実行されるようです@make_bold

これは、装飾された関数の場合、最初に関数を実行してから、他のデコレーターの上部に移動することを意味しますか?同様に@make_italicまず@make_bold、代わりに反対の。

これは、ほとんどのプログラミング言語でのトップダウンアプローチの標準とは異なることを意味しますか?このデコレータの場合だけですか?それとも私は間違っていますか?


4
はい、それはボトムアップから始まり、結果を次へ渡します
Padraic Cunningham

1
@PadraicCunninghamコメントも回答の重要な部分です。関連する問題(持っていたstackoverflow.com/questions/47042196/...
shookeesを

私はそれがトップダウンであるという意味で、まだトップダウンであると思いますa(b(x))(3行に分割することを想像する場合)
joel

回答:


126

デコレータは、装飾している関数をラップします。そのmake_boldためmake_italichello関数を装飾したデコレータの結果を装飾しました。

@decorator構文は、実際には単なるシンタックスシュガーです。以下:

@decorator
def decorated_function():
    # ...

実際には次のように実行されます:

def decorated_function():
    # ...
decorated_function = decorator(decorated_function)

元のdecorated_functionオブジェクトをdecorator()返されたもので置き換えます。

デコレータをスタックすると、そのプロセスが外側に繰り返されます。

だからあなたのサンプル:

@make_bold
@make_italic
def hello():
  return "hello world"

次のように展開できます。

def hello():
  return "hello world"
hello = make_bold(make_italic(hello))

ここで呼び出すと、実際にhello()はによって返されたオブジェクトが呼び出されmake_bold()ます。ラップされた関数を呼び出すmake_bold()a lambdaを返しましたmake_bold。これは、の戻り値ですmake_italic()。これは、元のを呼び出すラムダでもありhello()ます。あなたが得るこれらのすべての呼び出しを展開します:

hello() = lambda : "<b>" + fn() + "</b>" #  where fn() ->
    lambda : "<i>" + fn() + "</i>" # where fn() -> 
        return "hello world"

したがって、出力は次のようになります。

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"

わかります。しかし、この場合、ラッパーが2つある場合、IDEは最初のラッパーの結果を自動的に検出してラップするということですか。と思ったから @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)?これに基づいて、最初の結果をラップするかどうかはわかりません。または、この2つのラッパーの場合、IDEは、make_bold(make_italic(hello))私が共有したものの代わりに、あなたが述べたように使用しますか?
2014

3
@初心者:IDEはここでは何もしません。ラッピングを行うのはPythonです。最後のサンプルで、make_bold()ラップmake_italic()に使用されたの出力をラップすることを示したhelloので、と同等ですmake_bold(make_italic(hello))
Martijn Pieters

ラムダを使用しないこのコードのバージョンを提供できますか?.formatを試しましたが、機能しません。そして、なぜこの例でラムダが使用されているのですか?私はラムダとこの例でそれがどのように機能するかを理解しようとしていますが、まだ問題があります。ラムダは、def関数の標準と比較して非常に簡単に渡すことができる1行の関数のようなものだと思いますか?
2014

def inner: return "<b>" + fn() + "</b>"return inner場合、「通常の」関数バージョンになります。それほど大きな違いはありません。
Martijn Pieters

私はいつも秩序について混乱します。「...デコレータは、「def」ステートメントに最も近いものから適用され ます。これを「裏返しと呼びます。Martijnはこれを「外向き」と呼んでいると思います。この手段のmake_italic デコレータが前に実行されるmake_bold デコレータので、make_italicに最も近いですdef。しかし、私はそれを忘れて装飾されたコードの実行順序:make_bold 装飾された(すなわち太字ラムダ)を、最初に実行が続いているmake_italic 装飾されたラムダ(つまり、イタリックラムダ)。
赤エンドウ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.