Pythonの「__enter__」と「__exit__」について解説します。
-
22-09-2019 - |
質問
誰かのコードでこれを見ました。それはどういう意味ですか?
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
解決
これらの魔法のメソッドを使用して(__enter__
、__exit__
)は、with
文で簡単に使用できるオブジェクトを実装することができます。
アイデアは、それはいくつかの「cleandown」コード実行を(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 - すてきな過去記事を持っているの声明 ' 'と'同様ます。
他のヒント
何かわかっていれば コンテキストマネージャー では、それ以上理解する必要はありませんか __enter__
そして __exit__
魔法の方法。非常に簡単な例を見てみましょう。
この例では開いています マイファイル.txt の助けを借りて 開ける 関数。の 試してみて/ついに ブロックにより、予期しない例外が発生した場合でも確実に マイファイル.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)
コードを見ると、ファイルを閉じていないので、ファイルがありません。 試してみて/ついに ブロック。なぜなら と ステートメントは自動的に閉じます マイファイル.txt 。電話でも確認できます print(fp.closed)
属性 -- を返す True
.
これは、ファイル オブジェクト (この例では fp) が返されるためです。 開ける 関数には 2 つの組み込みメソッドがあります __enter__
そして __exit__
. 。コンテキストマネージャーとも呼ばれます。 __enter__
メソッドは開始時に呼び出されます と ブロックして、 __exit__
最後にメソッドが呼び出されます。注記: と ステートメントは、コンテキスト管理プロトコルをサポートするオブジェクトでのみ機能します。つまり、彼らは持っている __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__
魔法の方法。
は、私はそれが妙に難しいグーグルで__enter__
と__exit__
方法のためのpythonのドキュメントを検索したので、ヘルプ他の人にここにリンクがあります
https://docs.python.org/ 2 /参照/ datamodel.html#と文・コンテキスト・マネージャーの
https://docs.python.org/3/reference /datamodel.html#with-statement-context-managers の
(詳細は、両方のバージョンについても同様である)
object.__enter__(self)
このオブジェクトに関連したランタイム・コンテキストを入力します。with
文があれば、文のよう句で指定されたターゲット(複数可)に、このメソッドの戻り値をバインドします。例外は(すなわち、伝播されるのを防ぐ)供給され、この方法は、例外を抑制することを希望している場合、
object.__exit__(self, exc_type, exc_value, traceback)
このオブジェクトに関連したランタイム・コンテキストを終了します。パラメータは、コンテキストが終了したことが原因となった例外を説明します。コンテキストが例外なく終了した場合は、すべての3つの引数がNone
になります。、それは真の値を返す必要があります。そうでない場合、例外は、この方法から終了時に正常に処理されます。
__exit__()
方法はリレイズしないように注意してください渡された例外。これは、呼び出し側の責任です。
私は__exit__
メソッドの引数の明確な説明を期待していました。これが欠けているが、我々はそれらを推測することができます...
おそらくexc_type
が例外のクラスです。
それはあなたがリレイズ、渡されたべきではない例外と言います。これは、引数の一つは、実際の例外のインスタンスであるかもしれない...または多分あなたは型と値からそれを自分のインスタンスを作成することになっていることを私たちに示唆?
私たちは、この記事を見て、答えることができます。
http://effbot.org/zone/python-with-statement.htmする
たとえば、次のよう
__exit__
方法は、任意の例外TypeErrorを飲み込んだ、しかしを通じて他のすべての例外をすることができます:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
...そうはっきりvalue
は例外インスタンスです。
そしておそらくtraceback
であるA Pythonのトレースバックの対象ます。
起動順序を例示するために上記の回答に加えて、簡単な実行例
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
私の答え(学習についての私の考え)を追加してみてください:
__enter__
そして [__exit__]
どちらも、「」の本体への出入り時に呼び出されるメソッドです。with ステートメント" (PEP343)、両方の実装はコンテキスト マネージャーと呼ばれます。
with ステートメントは、tryfinally 節のフロー制御を隠し、コードをわかりにくくすることを目的としています。
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
サーバー側の結果は前と同じ
私の下手な英語と不明確な説明で申し訳ありませんが、ありがとうございます...