Python Language
コンテキストマネージャ( "with"ステートメント)
サーチ…
前書き
Pythonのコンテキストマネージャは広く使用されていますが、その使用目的の背後にある目的を理解する人はほとんどいません。ファイルの読み書きによく使用されるこれらのステートメントは、システムメモリを節約し、特定のリソースが特定のプロセスでのみ使用されていることを保証することによってリソース管理を向上させます。このトピックでは、Pythonのコンテキストマネージャーの使い方を説明しています。
構文
- "context_manager"( "エイリアス"として)(、 "context_manager"( "エイリアス"として)?)*:
備考
コンテキストマネージャは、 PEP 343で定義されています。それらは、リソース管理のためのより簡潔なメカニズムとして、 try ... finally
構築するtry ... finally
よりも使用されることが意図されています。正式な定義は以下の通りです。
このPEPでは、コンテキストマネージャは、
__enter__()
および__exit__()
メソッドを提供しています。__enter__()
メソッドは、with文の本体への__exit__()
時に呼び出されます。
次に、 with
ステートメントを次のように定義します。
with EXPR as VAR: BLOCK
上記の声明の翻訳は:
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)
コンテキストマネージャーとwithステートメント
コンテキストマネージャは、コンテキスト(コードブロック)の開始時と終了時に通知されるオブジェクトです。通常、 with
ステートメントで1つを使用します。それは通知の世話をします。
たとえば、ファイルオブジェクトはコンテキストマネージャです。コンテキストが終了すると、ファイルオブジェクトは自動的に閉じられます。
open_file = open(filename)
with open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
上記の例は、通常、 as
キーワードを使用して簡略化されていas
。
with open(filename) as open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
ブロックの実行が終了すると、コンテキストマネージャのexitメソッドが呼び出されます。これには例外が含まれており、エラーにより開いているファイルや接続が途中で終了する場合に役立ちます。ファイルや接続を適切に閉じないでスクリプトを終了すると、データが失われるなどの問題が発生する可能性があります。コンテキストマネージャを使用することにより、このような方法で損害や損失を防ぐための予防措置が常に講じられるようにすることができます。この機能はPython 2.5で追加されました。
ターゲットに割り当てる
多くのコンテキストマネージャは、入力時にオブジェクトを返します。そのオブジェクトをwith
ステートメントの新しい名前に割り当てることができます。
たとえば、 with
ステートメントでデータベース接続を使用すると、カーソルオブジェクトが得られます。
with database_connection as cursor:
cursor.execute(sql_query)
ファイルオブジェクトはそれ自身を返します。これにより、ファイルオブジェクトを開いて、1つの式でコンテキストマネージャとして使用することができます。
with open(filename) as open_file:
file_contents = open_file.read()
独自のコンテキストマネージャを書く
コンテキストマネージャは、 __enter__()
と__exit__()
2つのマジックメソッドを実装するオブジェクトです(他のメソッドも実装できます)。
class AContextManager():
def __enter__(self):
print("Entered")
# optionally return an object
return "A-instance"
def __exit__(self, exc_type, exc_value, traceback):
print("Exited" + (" (with an exception)" if exc_type else ""))
# return True if you want to suppress the exception
コンテキストが例外で終了した場合、その例外に関する情報はtriple exc_type
、 exc_value
、 traceback
(これらはsys.exc_info()
関数によって返されるものと同じ変数です)としてsys.exc_info()
れます。コンテキストが正常に終了する場合、これらの引数の3つはすべてNone
ます。
例外が発生し、 __exit__
メソッドに渡された場合、このメソッドは例外を抑制するためにTrue
を返すことができます。そうしないと、例外は__exit__
関数の最後に再発生します。
with AContextManager() as a:
print("a is %r" % a)
# Entered
# a is 'A-instance'
# Exited
with AContextManager() as a:
print("a is %d" % a)
# Entered
# Exited (with an exception)
# Traceback (most recent call last):
# File "<stdin>", line 2, in <module>
# TypeError: %d format: a number is required, not str
2番目の例では、with-statement本体の途中で例外が発生しても、例外が外部スコープに伝播する前に__exit__
ハンドラがまだ実行されていることに注意してください。
__exit__
メソッドだけが必要な場合は、コンテキストマネージャのインスタンスを返すことができます:
class MyContextManager:
def __enter__(self):
return self
def __exit__(self):
print('something')
ジェネレータ構文を使用して独自のコンテキストマネージャを作成する
contextlib.contextmanager
デコレータのおかげで、ジェネレータ構文を使ってコンテキストマネージャを書くこともできます:
import contextlib
@contextlib.contextmanager
def context_manager(num):
print('Enter')
yield num + 1
print('Exit')
with context_manager(2) as cm:
# the following instructions are run when the 'yield' point of the context
# manager is reached.
# 'cm' will have the value that was yielded
print('Right in the middle with cm = {}'.format(cm))
次を生成する:
Enter
Right in the middle with cm = 3
Exit
デコレータは、ジェネレータを1に変換することによってコンテキストマネージャを作成する作業を簡素化します。 yield式の前のすべてが__enter__
メソッドになり、 __enter__
された値はジェネレータによって返された値になります(withステートメントの変数にバインドできます) __exit__
式の後のすべてが__exit__
メソッドになります。
例外は、コンテキストマネージャによって処理する必要がある場合、 try..except..finally
-blockは、発電機で書くことができ、中に発生したすべての例外with
-ブロックは、この例外ブロックによって処理されます。
@contextlib.contextmanager
def error_handling_context_manager(num):
print("Enter")
try:
yield num + 1
except ZeroDivisionError:
print("Caught error")
finally:
print("Cleaning up")
print("Exit")
with error_handling_context_manager(-1) as cm:
print("Dividing by cm = {}".format(cm))
print(2 / cm)
これにより、
Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit
複数のコンテキストマネージャ
複数のコンテンツマネージャを同時に開くことができます。
with open(input_path) as input_file, open(output_path, 'w') as output_file:
# do something with both files.
# e.g. copy the contents of input_file into output_file
for line in input_file:
output_file.write(line + '\n')
コンテキストマネージャのネストと同じ効果があります。
with open(input_path) as input_file:
with open(output_path, 'w') as output_file:
for line in input_file:
output_file.write(line + '\n')
リソースの管理
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.open_file = open(self.filename, self.mode)
return self.open_file
def __exit__(self, *args):
self.open_file.close()
__init__()
メソッドはオブジェクトを設定します。この場合、ファイル名とモードをオープンファイルに設定します。 __enter__()
が開き、ファイルと返す__exit__()
だけで、それをクローズします。
これらのマジックメソッド( __enter__
、 __exit__
)を使うwith
、withステートメントで簡単に使用できるオブジェクトを実装できます。
ファイルクラスを使用する:
for _ in range(10000):
with File('foo.txt', 'w') as f:
f.write('foo')