2017-05-16 12 views
9

次のPython 2.xコードスニペットを検討してください。私は、具体的__exit__呼び出しに関する興味with文とコンテキストマネージャの__exit__メソッドからの復帰

Line 1 from file 
Before del 
__exit__ called 
After del 

:このスクリプトの

from __future__ import print_function 


class myfile(file): 
    def __exit__(self, *excinfo): 
     print("__exit__ called") 
     super(myfile, self).__exit__(*excinfo) 


def my_generator(file_name): 
    with myfile(file_name) as fh: 
     for line in fh: 
      yield line.strip() 


gen = my_generator('file.txt') 
print(next(gen)) 
print("Before del") 
del gen 
print("After del") 

出力がある(与えられたfile.txtなどは、複数のラインを持っています)。

彼のメソッドの実行をトリガするものはありますか?私たちが知る限り、コードは決してwithのステートメントを残しませんでした(yieldのステートメントの後に "停止"していました)。ジェネレータの参照カウントが0になったときに__exit__が呼び出されることが保証されていますか?

+2

2回目の次のコールは、not-yet-minimalの例から残ったものでした。私は固定コードスニペット、それは今正確です。 –

+1

この質問はCPythonについてのみですか? – MSeifert

+0

意味的には、「最終的に」https://www.python.org/dev/peps/pep-0343/ –

答えて

4

、Pythonの呼び出しを、あなたのmyfile.__exit__方法でargcを印刷する場合は、あなたは、コンテキストが自然に閉じられていなかったことがわかりますそのcloseメソッドを使用して、最後にyieldの時点でGeneratorExit例外を発生させます(まだ実行が完了していない場合)。このGeneratorExitが伝播すると、使用したコンテキストマネージャのメソッド__exit__がトリガされます。

これは、Python 2.5、in the same PEP as send and yield expressionsで導入されました。それ以前は、のにはfinallywithの文が2.5よりも前に存在していた場合、yieldは内部には届きませんでした。

1

@user2357112's answerに追加する場合は、内部で例外が発生するとブロックwithが中断します。この例外は、コンテキスト用に作成されたオブジェクトのメソッド__exit__に渡されます。

クラスは、何も通知しないので、GeneratorExit例外を黙って渡しているようです。スクリプトの

class myfile(file): 
    def __exit__(self, *excinfo): 
     print("__exit__ called") 
     print(excinfo[0]) # Print the reason why the context exited 
     super(myfile, self).__exit__(*excinfo) 

出力:ジェネレータオブジェクトの再利用に

Line 1 from file 
Before del 
__exit__ called 
<type 'exceptions.GeneratorExit'> 
After del 
関連する問題