pythonコンテキストマネージャ(context manager)
8383 ワード
まず、コンテキストマネージャとは?コンテキストマネージャは、コンテキスト管理プロトコルを実装するオブジェクトです.主に各種のグローバル状態の保存と復元、ファイルの閉じるなどに用いられ、コンテキストマネージャ自体が装飾器である.次のセクションでは、コンテキストマネージャについて説明します.
コンテキスト管理プロトコル
コンテキスト管理プロトコルには、次の2つの方法があります.
一般的なファイルの開く操作を見てみましょう
Open関数は、コンテキスト管理プロトコルを実装したファイルタイプ変数を返します.
with文
コンテキストマネージャといえばwith文を言わざるを得ません.なぜですか.with文はコンテキストマネージャをサポートするために存在するため、コンテキスト管理プロトコルを使用してコードブロック(with文体)の実行を包み、try...except...finallyは使いやすいパッケージを提供しています.with文の構文は次のとおりです.
withとasはキーワードであり、EXPRはコンテキスト式であり、任意の式(式であり、式リストではない)であり、VARは付与されたターゲット変数である.「as VAR」はオプションです.上記の文の最下位の実装は、次のように記述できます.
これでwith文の実行過程が明らかになります.コンテキスト式を実行し、コンテキストマネージャ を取得する.コンテキストマネージャの に備える.コンテキストマネージャを呼び出す with文に指定されたターゲット変数がある場合、 に割り当てられる.実行with文体 コンテキストマネージャの
複数の式をまとめることもできます.
それは
注意:マルチコンテキスト式はpython 2.7からサポートされています.
カスタムコンテキストマネージャ
この例ではwith文はasキーワードを用いず,現在のオブジェクトを返す.次に、現在のオブジェクトではないオブジェクトを返す例を見てみましょう.
次に異常処理の例を見てみましょう
contextlibモジュール
コンテキストの管理についてpythonは、同じメカニズムを実装するために組み込まれたモジュールcontextlibも提供し、ジェネレータと装飾器によって実装されるコンテキストマネージャは、with文と手動でコンテキスト管理プロトコルを実装するよりも優雅に見えます.contextlibは、異なるシーンでそれぞれ使用される3つの相関関数を提供します.
contextlib.contextmanager(func)
コンテキスト管理プロトコルを手動で実装するのは難しくありませんが、コンテキスト管理プロトコルを実装する必要がなく、contextmanagerを使用してジェネレータをコンテキストマネージャに変換することができます.
contextlib.nested(mgr1[, mgr2[, ...]])
複数のコンテキストを同時に管理する必要がある場合があります.この場合、nestedを使用して、複数のコンテキストマネージャをネストされた上下マネージャに結合できます.
に等しい
python 2.7以降、withは複数のコンテキストの直接ネスト操作をサポートしているので、nestedはもう使用することをお勧めしません.
contextlib.closing(thing)
本明細書の最初の例から分かるように、ファイルクラスはコンテキスト管理プロトコルをサポートし、with文を直接使用することができ、一部のオブジェクトはプロトコルをサポートしていないが、使用時に正常に終了することを確保し、closingを使用してコンテキストマネージャを作成することができる.それは
ただし、closingの使用は次のようになります.
ページを閉じる必要はありません.異常を投げてもページは正常に閉じます.
注意:with文体(with statement body)with文では、withに包まれた文体には非常に正式な名前はありませんが、一般的にはそう呼ばれています.
reference https://www.python.org/dev/peps/pep-0343/ https://docs.python.org/2.7/library/stdtypes.html#typecontextmanager https://docs.python.org/2.7/reference/compound_stmts.html#with https://docs.python.org/2.7/library/contextlib.html?highlight=contextlib#contextlib.contextmanager https://pymotw.com/2/contextlib/
コンテキスト管理プロトコル
コンテキスト管理プロトコルには、次の2つの方法があります.
contextmanager.__enter__()
メソッドからランタイムコンテキストに入り、現在のオブジェクトまたはランタイムコンテキストに関連する他のオブジェクトを返します.with文にasキーワードが存在する場合、戻り値はas後の変数にバインドされます.contextmanager.__exit__(exc_type, exc_val, exc_tb)
実行時コンテキストを終了し、処理する必要がある異常があるかどうかを示すブール値を返します.with文体の実行中に異常が発生した場合、終了時にパラメータに異常タイプ、異常値、異常追跡情報が含まれます.そうしないと、3つのパラメータはNoneになります.一般的なファイルの開く操作を見てみましょう
>>> with open("/etc/hosts", "r") as file:
... dir(file)
...
['__class__', '__delattr__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'closed', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'mode', 'name', 'newlines', 'next', 'read', 'readinto', 'readline', 'readlines', 'seek', 'softspace', 'tell', 'truncate', 'write', 'writelines', 'xreadlines']
Open関数は、コンテキスト管理プロトコルを実装したファイルタイプ変数を返します.
with文
コンテキストマネージャといえばwith文を言わざるを得ません.なぜですか.with文はコンテキストマネージャをサポートするために存在するため、コンテキスト管理プロトコルを使用してコードブロック(with文体)の実行を包み、try...except...finallyは使いやすいパッケージを提供しています.with文の構文は次のとおりです.
with EXPR as VAR:
BLOCK
withとasはキーワードであり、EXPRはコンテキスト式であり、任意の式(式であり、式リストではない)であり、VARは付与されたターゲット変数である.「as VAR」はオプションです.上記の文の最下位の実装は、次のように記述できます.
mgr = (EXPR)
exit = type(mgr).__exit__ #
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # "as VAR"
BLOCK
except:
#
exc = False
if not exit(mgr, *sys.exc_info()):
raise
# __exit__ false, ; ,
finally:
if exc:
exit(mgr, None, None, None)
これでwith文の実行過程が明らかになります.
__exit__()
メソッドをロードして、後期呼び出し__enter__()
メソッド__enter__()
メソッドから取得された関連オブジェクトは、ターゲット変数__exit__()
メソッドを呼び出します.with文体による異常終了であれば、異常タイプ、異常値、異常追跡情報は__exit__()
に渡されます.そうでなければ、3つのパラメータはNoneです.複数の式をまとめることもできます.
with A() as a, B() as b:
BLOCK
それは
with A() as a:
with B() as b:
BLOCK
注意:マルチコンテキスト式はpython 2.7からサポートされています.
カスタムコンテキストマネージャ
class ContextManager(object):
def __init__(self):
print '__init__()'
def __enter__(self):
print '__enter__()'
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print "__exit__()"
with ContextManager():
print "OK, we can do something here~~"
#
__init__()
__enter__()
OK, we can do something here~~
__exit__()
この例ではwith文はasキーワードを用いず,現在のオブジェクトを返す.次に、現在のオブジェクトではないオブジェクトを返す例を見てみましょう.
class InnerContext(object):
def __init__(self, obj):
print 'InnerContext.__init__(%s)' % obj
def do_something(self):
print 'InnerContext.do_something()'
def __del__(self):
print 'InnerContext.__del__()'
class ContextManager(object):
def __init__(self):
print 'ContextManager.__init__()'
def __enter__(self):
print 'ContextManager.__enter__()'
return InnerContext(self)
def __exit__(self, exc_type, exc_val, exc_tb):
print "ContextManager.__exit__()"
with ContextManager() as obj:
obj.do_something()
print "OK, we can do something here~~"
#
ContextManager.__init__()
ContextManager.__enter__()
InnerContext.__init__(<__main__.contextmanager object="" at="">)
InnerContext.do_something()
OK, we can do something here~~
ContextManager.__exit__()
InnerContext.__del__()
次に異常処理の例を見てみましょう
class ContextManager(object):
def __init__(self, flag):
print 'ContextManager.__init__(%s)' % flag
self.flag = flag
def __enter__(self):
print 'ContextManager.__enter__()'
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print 'ContextManager.__exit__(%s, %s, %s)' % (exc_type, exc_val, exc_tb)
return self.flag
with ContextManager(True):
raise RuntimeError('error message handled')
print
with ContextManager(False):
raise RuntimeError('error message propagated')
#
ContextManager.__init__(True)
ContextManager.__enter__()
ContextManager.__exit__(, error message handled, )
ContextManager.__init__(False)
ContextManager.__enter__()
ContextManager.__exit__(, error message propagated, )
Traceback (most recent call last):
File "ContextManager.py", line 19, in
raise RuntimeError('error message propagated')
RuntimeError: error message propagated
contextlibモジュール
コンテキストの管理についてpythonは、同じメカニズムを実装するために組み込まれたモジュールcontextlibも提供し、ジェネレータと装飾器によって実装されるコンテキストマネージャは、with文と手動でコンテキスト管理プロトコルを実装するよりも優雅に見えます.contextlibは、異なるシーンでそれぞれ使用される3つの相関関数を提供します.
contextlib.contextmanager(func)
コンテキスト管理プロトコルを手動で実装するのは難しくありませんが、コンテキスト管理プロトコルを実装する必要がなく、contextmanagerを使用してジェネレータをコンテキストマネージャに変換することができます.
import contextlib
@contextlib.contextmanager
def createContextManager(name):
print '__enter__ %s' % name
yield name
print '__exit__ %s' % name
with createContextManager('foo') as value:
print value
#
__enter__ foo
foo
__exit__ foo
contextlib.nested(mgr1[, mgr2[, ...]])
複数のコンテキストを同時に管理する必要がある場合があります.この場合、nestedを使用して、複数のコンテキストマネージャをネストされた上下マネージャに結合できます.
with contextlib.nested(createContextManager('a'),createContextManager('b'),createContextManager('c')) as (a, b, c):
print a, b, c
#
__enter__ a
__enter__ b
__enter__ c
a b c
__exit__ c
__exit__ b
__exit__ a
に等しい
with createContextManager('a') as a:
with createContextManager('b') as b:
with createContextManager('c') as c:
print a, b, c
#
__enter__ a
__enter__ b
__enter__ c
a b c
__exit__ c
__exit__ b
__exit__ a
python 2.7以降、withは複数のコンテキストの直接ネスト操作をサポートしているので、nestedはもう使用することをお勧めしません.
with createContextManager('a') as a,createContextManager('b') as b,createContextManager('c') as c:
print a, b, c
#
__enter__ a
__enter__ b
__enter__ c
a b c
__exit__ c
__exit__ b
__exit__ a
contextlib.closing(thing)
本明細書の最初の例から分かるように、ファイルクラスはコンテキスト管理プロトコルをサポートし、with文を直接使用することができ、一部のオブジェクトはプロトコルをサポートしていないが、使用時に正常に終了することを確保し、closingを使用してコンテキストマネージャを作成することができる.それは
from contextlib import contextmanager
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
ただし、closingの使用は次のようになります.
from contextlib import closing
import urllib
with closing(urllib.urlopen('http://www.python.org')) as page:
for line in page:
print line
ページを閉じる必要はありません.異常を投げてもページは正常に閉じます.
注意:with文体(with statement body)with文では、withに包まれた文体には非常に正式な名前はありませんが、一般的にはそう呼ばれています.
reference