478

私はこれを誰かのコードで見ました。どういう意味ですか?

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.stream.close()

from __future__ import with_statement#for python2.5 

class a(object):
    def __enter__(self):
        print 'sss'
        return 'sss111'
    def __exit__(self ,type, value, traceback):
        print 'ok'
        return False

with a() as s:
    print s


print s
4

7 に答える 7

554

これらの魔法のメソッド(__enter__、 )を使用すると、ステートメント__exit__で簡単に使用できるオブジェクトを実装できます。with

アイデアは、実行される「クリーンダウン」コードを必要とするコードを簡単に構築できるようにすることです(try-finallyブロックと考えてください)。ここでもう少し説明します

有用な例としては、データベース接続オブジェクトがあります(対応する'with'ステートメントがスコープ外になると自動的に接続を閉じます)。

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

上で説明したように、このオブジェクトをステートメントで使用します( Python 2.5を使用している場合は、ファイルの先頭でwith実行する必要がある場合があります)。from __future__ import with_statement

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343-'with'ステートメント'にも素晴らしい記述があります。

于 2009-12-31T07:13:06.087 に答える
99

コンテキストマネージャーが何であるかを知っている場合は__enter____exit__メソッドを理解するためにこれ以上何もする必要はありません。非常に簡単な例を見てみましょう。

この例では、 open関数を使用してmyfile.txtファイルを開いています。try / finallyブロックは、予期しない例外が発生した場合でもmyfile.txtが閉じられるようにします。

fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

今私はステートメントで同じファイルを開いています:

with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
    for line in fp:
        print(line) 

コードを見ると、ファイルを閉じていません。try/finallyブロックはありません。withステートメントはmyfile.txtを自動的に閉じる ためです。print(fp.closed)属性--を呼び出すことで確認することもできます。これは。を返しますTrue

これは、open関数によって返されるファイルオブジェクト(私の例ではfp)に2つの組み込みメソッド__enter__とが含まれているため__exit__です。コンテキストマネージャーとも呼ばれます。__enter__メソッドはwithブロックの最初に呼び出され、__exit__ メソッドは最後に呼び出されます。

注:withステートメントは、コンテキスト管理プロトコルをサポートするオブジェクト(つまり、オブジェクト__enter____exit__メソッド)でのみ機能します。両方のメソッドを実装するクラスは、コンテキストマネージャークラスと呼ばれます。

次に、独自のコンテキストマネージャークラスを定義しましょう。

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'\n')
    def __enter__(self):
        print("__enter__")
        self.fp=open(self.filename,"a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
        self.fp.close()

with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

__enter__これで、魔法の方法の両方と魔法の方法の基本を理解できたと思います__exit__

于 2016-07-31T15:29:36.903 に答える
78

__enter__グーグルでPythonのドキュメントとメソッドを見つけるのは奇妙なことに難しい__exit__ので、他の人を助けるためにここにリンクがあります:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(詳細は両方のバージョンで同じです)

object.__enter__(self)
このオブジェクトに関連するランタイムコンテキストを入力します。ステートメントは、このwithメソッドの戻り値を、ステートメントのas句で指定されているターゲット(存在する場合)にバインドします。

object.__exit__(self, exc_type, exc_value, traceback)
このオブジェクトに関連するランタイムコンテキストを終了します。パラメータは、コンテキストが終了する原因となった例外を記述します。コンテキストが例外なく終了した場合、3つの引数はすべて。になりますNone

例外が提供され、メソッドが例外を抑制したい場合(つまり、例外が伝播されないようにしたい場合)、真の値を返す必要があります。それ以外の場合、例外はこのメソッドの終了時に通常どおり処理されます。

__exit__()メソッドは渡された例外を再発生させてはならないことに注意してください。これは発信者の責任です。

__exit__メソッド引数の明確な説明を期待していました。これは欠けていますが、私たちはそれらを推測することができます...

おそらくexc_type例外のクラスです。

渡された例外を再発生させてはならないということです。これは、引数の1つが実際のExceptionインスタンスである可能性があることを示唆しています...または、型と値から自分でインスタンス化することになっているのでしょうか?

この記事を見て答えることができます:http:
//effbot.org/zone/python-with-statement.htm

たとえば、次の__exit__メソッドはTypeErrorを飲み込みますが、他のすべての例外を許可します。

def __exit__(self, type, value, traceback):
    return isinstance(value, TypeError)

...明らかにvalue例外インスタンスです。

そして、おそらくtracebackPythonトレースバックオブジェクトです。

于 2017-06-06T10:23:49.663 に答える
69

呼び出し順序を例示する上記の回答に加えて、簡単な実行例

class myclass:
    def __init__(self):
        print("__init__")

    def __enter__(self): 
        print("__enter__")

    def __exit__(self, type, value, traceback):
        print("__exit__")

    def __del__(self):
        print("__del__")

with myclass(): 
    print("body")

出力を生成します:

__init__
__enter__
body
__exit__
__del__

注意:構文を使用するwith myclass() as mcと、変数mcは__enter__()、上記の場合、によって返される値を取得しますNone。このような使用には、次のような戻り値を定義する必要があります。

def __enter__(self): 
    print('__enter__')
    return self
于 2018-01-08T16:39:47.353 に答える
6

これはコンテキストマネージャーと呼ばれ、他のプログラミング言語にも同様のアプローチが存在することを付け加えたいと思います。それらを比較すると、Pythonのコンテキストマネージャーを理解するのに役立つ可能性があります。基本的に、コンテキストマネージャーは、初期化して、ある時点で破棄(破棄)する必要のあるリソース(ファイル、ネットワーク、データベース)を処理するときに使用されます。Java 7以降では、次の形式の自動リソース管理があります。

//Java code
try (Session session = new Session())
{
  // do stuff
}

Sessionは、AutoClosableまたはその(多くの)サブインターフェースの1つを実装する必要があることに注意してください。

C#では、次の形式のリソースを管理するためのステートメントを使用しています。

//C# code
using(Session session = new Session())
{
  ... do stuff.
}

Session実装する必要がありますIDisposable

Pythonでは、使用するクラスはとを実装する必要が__enter__あり__exit__ます。したがって、次の形式を取ります。

#Python code
with Session() as session:
    #do stuff

また、他の人が指摘しているように、すべての言語でいつでもtry / finalステートメントを使用して、同じメカニズムを実装できます。これは単なる構文糖衣です。

于 2020-02-20T00:15:34.270 に答える
5

私の答えを追加してみてください(私の学習の考え):

__enter__どちらも「 withステートメント」(PEP 343[__exit__] )の本体に出入りするときに呼び出されるメソッドであり、両方の実装はコンテキストマネージャーと呼ばれます。

withステートメントは、try finally節のフロー制御を非表示にして、コードを不可解にすることを目的としています。

withステートメントの構文は次のとおりです。

with EXPR as VAR:
    BLOCK

これは(PEP 343で言及されているように)次のように解釈されます。

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

いくつかのコードを試してください:

>>> import logging
>>> import socket
>>> import sys

#server socket on another terminal / python interpreter
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.listen(5)
>>> s.bind((socket.gethostname(), 999))
>>> while True:
>>>    (clientsocket, addr) = s.accept()
>>>    print('get connection from %r' % addr[0])
>>>    msg = clientsocket.recv(1024)
>>>    print('received %r' % msg)
>>>    clientsocket.send(b'connected')
>>>    continue

#the client side
>>> class MyConnectionManager:
>>>     def __init__(self, sock, addrs):
>>>         logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
>>>         : %(levelname)s --> %(message)s')
>>>         logging.info('Initiating My connection')
>>>         self.sock = sock
>>>         self.addrs = addrs
>>>     def __enter__(self):
>>>         try:
>>>             self.sock.connect(addrs)
>>>             logging.info('connection success')
>>>             return self.sock
>>>         except:
>>>             logging.warning('Connection refused')
>>>             raise
>>>     def __exit__(self, type, value, tb):
>>>             logging.info('CM suppress exception')
>>>             return False
>>> addrs = (socket.gethostname())
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> with MyConnectionManager(s, addrs) as CM:
>>>     try:
>>>         CM.send(b'establishing connection')
>>>         msg = CM.recv(1024)
>>>         print(msg)
>>>     except:
>>>         raise
#will result (client side) :
2018-12-18 14:44:05,863         : INFO --> Initiating My connection
2018-12-18 14:44:05,863         : INFO --> connection success
b'connected'
2018-12-18 14:44:05,864         : INFO --> CM suppress exception

#result of server side
get connection from '127.0.0.1'
received b'establishing connection'

そして今、手動で試してください(変換構文に従って):

>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make new socket object
>>> mgr = MyConnection(s, addrs)
2018-12-18 14:53:19,331         : INFO --> Initiating My connection
>>> ext = mgr.__exit__
>>> value = mgr.__enter__()
2018-12-18 14:55:55,491         : INFO --> connection success
>>> exc = True
>>> try:
>>>     try:
>>>         VAR = value
>>>         VAR.send(b'establishing connection')
>>>         msg = VAR.recv(1024)
>>>         print(msg)
>>>     except:
>>>         exc = False
>>>         if not ext(*sys.exc_info()):
>>>             raise
>>> finally:
>>>     if exc:
>>>         ext(None, None, None)
#the result:
b'connected'
2018-12-18 15:01:54,208         : INFO --> CM suppress exception

以前と同じサーバー側の結果

私の悪い英語と私の不明瞭な説明をお詫びします、ありがとう....

于 2018-12-18T08:08:33.053 に答える
2

Pythonは__enter__、実行がwithステートメントのコンテキストに入り、リソースを取得するときに呼び出します。実行が再びコンテキストを離れると、Pythonは__exit__リソースを解放するために呼び出します

Pythonのコンテキストマネージャーと「with」ステートメントについて考えてみましょう。Context Managerは、withステートメントで使用できるように、オブジェクトが従う必要のある単純な「プロトコル」(またはインターフェース)です。基本的に、オブジェクトをコンテキストマネージャーとして機能させたい場合は、オブジェクトにenterメソッドとexitメソッドを追加するだけです。Pythonは、リソース管理サイクルの適切なタイミングでこれら2つのメソッドを呼び出します。

これが実際にどのように見えるかを見てみましょう。open()コンテキストマネージャーの簡単な実装は次のようになります。

class ManagedFile:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        self.file = open(self.name, 'w')
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()

ManagedFileクラスは、コンテキストマネージャープロトコルに従い、withステートメントをサポートするようになりました。

>>> with ManagedFile('hello.txt') as f:
...    f.write('hello, world!')
...    f.write('bye now')`enter code here`

Python呼び出しは、実行がwithステートメントのコンテキストに入り、リソースを取得するときに開始されます。実行が再びコンテキストを離れると、Pythonはexitを呼び出してリソースを解放します。

Pythonでwithステートメントをサポートする方法は、クラスベースのコンテキストマネージャーを作成することだけではありません。標準ライブラリのcontextlibユーティリティモジュールは、基本的なコンテキストマネージャプロトコルの上に構築されたいくつかの抽象化を提供します。これにより、ユースケースがcontextlibによって提供されるものと一致する場合、あなたの生活が少し楽になります。

于 2020-11-21T15:51:19.867 に答える