2016-08-15 15 views
0

初心者Javaの背景から来たPythonコーダー。私はまだこれにより困惑しています:"with"、コンテキストマネージャー、python:単純な言葉で何が起こっていますか?

with open(...) as f: 
    do_something(f) 

もグーグルとここに答えのいくつかを読んだ後(私はちょうどそれらのまわりで私の頭を取得できませんでした)。

私が理解していることは、作成されたファイルへの参照を含む何らかの種類のラッパーであるコンテキストマネージャと呼ばれることです。上記

import numpy as np 

の下にそれはちょうどエイリアスだ 'と' のようである 'として'

as f: 

について。 'f'はファイルを参照するのではなく、コンテキストマネージャーを参照します。コンテキストマネージャは、デコレータパターンを使用して、開いているファイルのすべてのメソッドを実装しています。そのため、ファイルオブジェクトのように扱うことができます(ファイルに対して呼び出される適切なメソッドを呼び出してファイルオブジェクトを取得できますコンテキストマネージャ内)もちろん、ブロックが完了するとファイルは閉じられます(この点全体)。

これは質問に答える:open()は一般にファイル(またはファイルへの参照)またはコンテキストマネージャを返すか?一般的にコンテキストマネージャを返すのでしょうか?これは私たちがそれを知らずにいつも使ってきたことですか?あるいは、コンテキストマネージャのような何かを返すときは、この特別なコンテキスト以外のファイルタイプを返します。

これはどこに近いですか?誰でも明確にしたいですか?

+1

ファイル*はコンテキストマネージャです。 – user2357112

+0

オープン後の 'with'は、書き込み後にファイルを安全に閉じます。 – n1c9

+1

https://www.python.org/dev/peps/pep-0343/を読んだことがありますか? –

答えて

3

ファイルオブジェクトは、それぞれ__enter__とというメソッドを持っている点で、コンテキストマネージャです。 withは、__enter____exit__をそれぞれ呼び出すことによってコンテキストが入力および終了されたときにfileオブジェクトに通知します。これは、ファイルオブジェクトがファイルを閉じる方法を「知っている」方法です。ここにはラッパーオブジェクトは含まれていません。ファイルオブジェクトはこれらの2つのメソッドを提供します(Javaオブジェクトの場合、ファイルオブジェクトはコンテキストマネージャインタフェースを実装していると言えます)。

asではありません。ではなく、import module as altnameと同じエイリアスです。代わりにの戻り値contextmanager.__enter__()に割り当てられます。fileobject.__enter__()はこれをしなかったが、どちらかがNoneまたは別のオブジェクトを返した場合は、open()をインライン化できませんでし

with open(...) as fileobj: 

fileobject.__enter__()方法は、簡単な構文を使用できるようにすること、(そのファイルオブジェクト自体)selfを返します。コール;何が停止しないことを

fileobj = open(...) 
with fileobj as something_enter_returned: 
    fileobj.write() 

または

fileobj = open(...) 
with fileobj: # no as, ignore whatever fileobj.__enter__() produced 
    fileobj.write() 

注:返されたファイルオブジェクトへの参照を維持するためには、まずコンテキストマネージャとしてそれを使用する前に、変数にopen()の結果を代入する必要があると思いますあなた自身のコードで後者のパターンを使用することはできません。既にファイルオブジェクトへの別の参照がある場合は、にはがあり、as targetの部分を使用することはできません。ファイルオブジェクトにさらにアクセスする必要はありません。

しかし、他のコンテキストマネージャーは別のものを返す可能性があります。一部のデータベースコネクタは、データベースカーソルを返す:

conn = database.connect(....) 
with conn as cursor: 
    cursor.execute(...) 

とコンテキストを出て行くには、トランザクションが(天気を上かによって例外が発生した)コミットまたはロールバックされます。

0

コンテキストマネージャは、かなりシンプルな獣です...それらは単にtwo separate methods__enter____exit__)を定義するクラスです。 から返されるものは、withステートメントが実行されたときにwithステートメントのas x句にバインドされます。

はここで本当に愚かな例です:

>>> class CM(object): 
... def __enter__(self): 
...  print('In __enter__') 
...  return 'Hello world' 
... def __exit__(self, *args): 
...  print('In __exit__') 
... 
>>> with CM() as x: 
... print(x) 
... 
In __enter__ 
Hello world 
In __exit__ 

あなたが頻繁に単に__enter__方法からselfを返すコンテキストマネージャを参照してくださいよ、私はあなたがする必要がないことを証明するために、上記の例を書きました。また、あなたがwith文でコンテキストマネージャを構築する必要はありません、あなたは事前にそれを構築することができることに注意してください。

cm = CM() 
with cm as x: 
    ... 

コンテキストマネージャの理由は、Python、withの文と一緒に使用する場合ということですwithスイート内で例外が発生しても、__exit__が呼び出されることが保証されます。。

fileオブジェクトがコンテキストマネージャであるのでfileオブジェクト(これらはよく__enter____exit__方法を定義した)コンテキストマネージャAPIを使用して実装されています。 withステートメントで使用すると、pythonはwithスイートが終了するとファイルが閉じられることを保証します。

致命的なシステム障害を排除します。あなたのコンピュータが爆破した場合...

+0

__enter__と__exit__メソッドを実装しているクラスはすべてコンテキストマネージャです。私は継承やインタフェースの実装を考えているので混乱していますが、Fileの場合はこれを見ていません。 –

+0

@Detroitteatime - 私は理解しているか分からない。 python2.5 - > python2.7 'help(file)'と入力すると、そこに '__enter__'と' __exit__'が表示されます。恐らく、 'open'はファイルオブジェクトを返すファクトリメソッドであるという混乱がありますか? – mgilson

+0

@Detroitteatime:あなたはコンテキストマネージャをとにかくインターフェースとして考えることができます。ファイルオブジェクトはそのインタフェースを実装します。 –

1

これは、あなたが作成することができ、最も基本的なコンテキストマネージャである:

class UselessContextManager(object): 
    def __enter__(self): 
     pass 

    def __exit__(self, type, value, traceback): 
     pass 

with UselessContextManager() as nothing: 
    print(nothing is None) 

あなたが実際にプロセスフローがどのように見えるために少し感触を取得したい場合は、してみてください。この1:

class PrintingContextManager(object):           
    def __init__(self, *args, **kwargs):          
     print('Initializing with args: {} and kwargs: {}'.format(args, kwargs)) 

    def __enter__(self):              
     print('I am entering the context')          
     print('I am returning 42')            
     return 42                

    def __exit__(self, type, value, traceback):         
     print('And now I am exiting')           


print('Creating manager')              
manager = PrintingContextManager()            
print('Entering with block')             
with manager as fnord:               
    print('Fnord is {}'.format(fnord))           
    print('End of context')              
print('Out of context')               

出力:

Creating manager 
Initializing with args:() and kwargs: {} 
Entering with block 
I am entering the context 
I am returning 42 
Fnord is 42 
End of context 
And now I am exiting 
Out of context 

コードを次のように変更してください。 type, value, tracebackを印刷し、withブロック内で例外を発生させます。

>>> f = open('/tmp/spanish_inquisition.txt', 'w') 
>>> f.__enter__ 
<function TextIOWrapper.__enter__> 
>>> f.__exit__ 
<function TextIOWrapper.__exit__> 
:あなたはファイルは常にコンテキストマネージャであることを見ることができます

thing = ContextManager() 
try: 
    stuff = thing.__enter__() 
except Exception as e: 
    stuff.__exit__(type(e), e.args[0], e.__traceback__) 

Though truthfully it's a bit different

あなたが見ることができるように、with構文がためほとんどわずかです

ファイルがContextManagerである可能性がありますスーパークラスを継承せずに、あるいはインタフェースを明示的に実装することなく、2つのメソッドを実装するだけです。繰り返しますが、私はこの言語を初めて使う人です。

であり、明示的にインターフェイスを実装しています。 Javaでは、どのインタフェースを遵守するかを指定する必要があります。 Pythonでは、あなたはそれを行うだけです。ファイルのようなオブジェクトが必要ですか? .read()メソッドを追加します。たぶん、.seek(),.open()、および.close()が期待するものに依存します。しかし、Pythonでは...

it = DecoyDuck() 
if it.walks_like_a_duck() and it.talks_like_a_duck() and it.quacks_like_a_duck(): 
    print('It must be a duck') 
関連する問題