2016-02-18 8 views
5
import contextlib 
import time 

@contextlib.contextmanager 
def time_print(task_name): 
    t = time.time() 
    try: 
     yield 
    finally: 
     print task_name, "took", time.time() - t, "seconds." 


def doproc(): 
    x=1+1 


with time_print("processes"): 
    [doproc() for _ in range(500)] 

# processes took 15.236166954 seconds. 

このデコレータを使用するとdopocが実行されるのはいつですか?

+0

[docs:](https://docs.python.org/2/library/contextlib.html#contextlib。contextmanager) "ジェネレータが発生する時点で、with文にネストされたブロックが実行され、ブロックが終了した後にジェネレータが再開されます。ブロックで未処理の例外が発生した場合、どこで収穫が起こったのか」 –

+0

引数なしの 'yield'は意味的に' yield None'と等価です –

答えて

7

yield式は発電機を使用しているものは何でもに制御を返します。 @contextmanagerデコレータは、コードがセットアップ一部で行われていることを知っていることを意味し、この時点でを一時停止発電機、。

つまり、yieldの前にコンテキストマネージャ__enter__のフェーズを実行する必要があります。あなたのコンテキストが(そうwith文の下にブロックが行われます)出ると

@contextmanagerデコレータは、コンテキストマネージャプロトコルの__exit__一部に対して呼び出され、次の2つのいずれかを行います:場合

  • を例外なく、ジェネレータを再開します。だからあなたのジェネレータはyieldラインでunpauses、あなたはクリーンアップフェーズ、例外が発生した場合は、デコレータは発電機でその例外を発生させるgenerator.throw()を使用しています

  • 一部を入力してください。あたかもyield行がその例外を引き起こしたかのようになります。 finally句があるため、例外のためにジェネレータが終了する前に実行されます。

次のように、あなたの具体的な例では順序は次のとおりです。

  1. with time_print("processes"):

    これは、コンテキストマネージャを作成し、その上__enter__を呼び出します。

  2. ジェネレータが実行を開始し、t = time.time()が実行されます。

  3. yield式は発電機を一時停止し、制御が戻ってデコレータになります。これは、as targetの部分がある場合には、何でも得られたものを取り出してwithステートメントに返します。

  4. [doproc() for _ in range(500)]が実行されて完了します。

  5. コンテキストマネージャ__exit__方法が実行され、例外が渡されていない。

  6. デコレータが発電を再開し、それは中断したところ、それが継続します。

  7. finally:ブロックが入力され、print task_name, "took", time.time() - t, "seconds."が実行されます。

  8. 発電機が出て、デコレータ__exit__方法の出口は、すべてが行われます。

関連する問題