Pythonのキーワードwith詳細
6082 ワード
Python 2.5には、
このコードでは、
もう一つの例を見てみましょう.
データベース・トランザクション・リクエストを開始するときに、このようなコードが使用されることがよくあります.
トランザクション要求を開始する操作が
次に、
withの一般的な実行手順
基本的な
ここで、は、 コンテキストマネージャの コンテキストマネージャを呼び出す は、 コンテキストマネージャを呼び出す
このプロセスをコードで表します.
このプロセスにはいくつかの詳細があります.コンテキストマネージャに が
次に、上記のプロセスを2つの方法で実現しましょう.
コンテキストマネージャクラスの実装
第1の方法は、インスタンス属性
ジェネレータ装飾の使用
Pythonの標準ライブラリでは、ジェネレータを使用してコンテキストマネージャを取得できるアクセサリーがあります.ジェネレータアクセラレータを使用する手順は、次のとおりです.
第一目で見ると、このような実現方式はもっと簡単だが、そのメカニズムはもっと複雑だ.実行プロセスを見てみましょう. Python解釈器が デコレーション ヘルプ関数は、このジェネレータを 例外ジェネレータメソッドが発生しない場合は
上記の手順のコードの概要を見てみましょう.
まとめ
Pythonの
その他の例
ロック:
標準出力リダイレクト:
参照 The Python "with"Statement by Example PEP 343 元アドレス:Pythonのキーワードwith詳細-楽正
with
のキーワードが追加されています.それはよく使われるtry ... except ... finally ...
モードを便利に多重化する.最も古典的な例を見てみましょうwith open('file.txt') as f:
content = f.read()
このコードでは、
with
のコードブロックが実行中に発生した場合にかかわらず、ファイルは最終的に閉じられます.コードブロックが実行中に例外が発生した場合、この例外が投げ出される前に、プログラムは開いているファイルを閉じます.もう一つの例を見てみましょう.
データベース・トランザクション・リクエストを開始するときに、このようなコードが使用されることがよくあります.
db.begin()
try:
# do some actions
except:
db.rollback()
raise
finally:
db.commit()
トランザクション要求を開始する操作が
with
キーワードをサポートできるようになった場合、このようなコードを使用すればよい.with transaction(db):
# do some actions
次に、
with
の実行手順を詳細に説明し、上記のコードを2つの一般的な方法で実現する.withの一般的な実行手順
基本的な
with
式で、その構造は次のとおりです.with EXPR as VAR:
BLOCK
ここで、
EXPR
は任意の式であってもよい.as VAR
はオプションです.一般的な実行手順は次のとおりです.EXPR
を計算し、コンテキストマネージャを取得する.__exit()__
メソッドは、その後の呼び出しのために保存される.__enter()__
メソッド.with
式がas VAR
を含む場合、EXPR
の戻り値はVAR
に与えられる.BLOCK
の式を実行する.__exit()__
メソッド.BLOCK
の実行中に異常が発生してプログラムが終了すると、異常のtype
、value
、およびtraceback
(すなわち、sys.exc_info()
)がパラメータとして__exit()__
メソッドに渡される.そうでなければ、3つのNone
が渡されます.このプロセスをコードで表します.
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
finally:
if exc:
exit(mgr, None, None, None)
このプロセスにはいくつかの詳細があります.
__enter()__
または__exit()__
のいずれかの方法がない場合、解釈器はAttributeError
を放出する.BLOCK
で異常が発生した後、__exit()__
メソッドがTrue
とみなされる値を返すと、この異常は投げ出されず、後続のコードが実行され続けます.次に、上記のプロセスを2つの方法で実現しましょう.
コンテキストマネージャクラスの実装
第1の方法は、インスタンス属性
db
およびコンテキストマネージャに必要な方法__enter()__
および__exit()__
を含むクラスを実装することである.class transaction(object):
def __init__(self, db):
self.db = db
def __enter__(self):
self.db.begin()
def __exit__(self, type, value, traceback):
if type is None:
db.commit()
else:
db.rollback()
with
の実行プロセスを理解すると、この実装は容易に理解できる.以下で紹介する実装方式は,その原理を理解するには複雑である.ジェネレータ装飾の使用
Pythonの標準ライブラリでは、ジェネレータを使用してコンテキストマネージャを取得できるアクセサリーがあります.ジェネレータアクセラレータを使用する手順は、次のとおりです.
from contextlib import contextmanager
@contextmanager
def transaction(db):
db.begin()
try:
yield db
except:
db.rollback()
raise
else:
db.commit()
第一目で見ると、このような実現方式はもっと簡単だが、そのメカニズムはもっと複雑だ.実行プロセスを見てみましょう.
yield
キーワードを認識すると、def
は通常の関数に代わるジェネレータ関数を作成します(クラス定義以外では関数の代替方法が好きです).contextmanager
が呼び出され、呼び出された後にGeneratorContextManager
インスタンスが生成されるヘルプメソッドが返される.最終的なwith
式のEXPR
は、contentmanager
デザイナによって返されるヘルプ関数を呼び出す.with
式はtransaction(db)
を呼び出し、実際にはヘルプ関数を呼び出す.ヘルプ関数はジェネレータ関数を呼び出し、ジェネレータ関数はジェネレータを作成します.GeneratorContextManager
に渡し、GeneratorContextManager
のインスタンスオブジェクトをコンテキストマネージャとして作成します.with
式は、インスタンスオブジェクトのコンテキストマネージャの__enter()__
メソッドを呼び出す.__enter()__
メソッドでは、このジェネレータのnext()
メソッドが呼び出されます.このとき、ジェネレータメソッドはyield db
で停止し、db
をnext()
の戻り値とする.as VAR
がある場合、VAR
に割り当てられます.with
のうちBLOCK
が実行される.BLOCK
実行終了後、コンテキストマネージャの__exit()__
メソッドが呼び出される.__exit()__
メソッドは、ジェネレータのnext()
メソッドを再び呼び出す.StopIteration
異常が発生した場合、pass
.db.commit()
が実行され、そうでない場合はdb.rollback()
が実行されます.上記の手順のコードの概要を見てみましょう.
def contextmanager(func):
def helper(*args, **kwargs):
return GeneratorContextManager(func(*args, **kwargs))
return helper
class GeneratorContextManager(object):
def __init__(self, gen):
self.gen = gen
def __enter__(self):
try:
return self.gen.next()
except StopIteration:
raise RuntimeError("generator didn't yield")
def __exit__(self, type, value, traceback):
if type is None:
try:
self.gen.next()
except StopIteration:
pass
else:
raise RuntimeError("generator didn't stop")
else:
try:
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw()")
except StopIteration:
return True
except:
if sys.exc_info()[1] is not value:
raise
まとめ
Pythonの
with
式には多くのPython特性が含まれている.時間をかけてwith
を食べるのはとても価値のあることです.その他の例
ロック:
@contextmanager
def locked(lock):
lock.acquired()
try:
yield
finally:
lock.release()
標準出力リダイレクト:
@contextmanager
def stdout_redirect(new_stdout):
old_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield
finally:
sys.stdout = old_stdout
with open("file.txt", "w") as f:
with stdout_redirect(f):
print "hello world"
参照