aobako.net
Python における with 文の使用方法
March 21, 2019
比較的よく見かける with 文ですが、使用箇所から感じるなんとなくの雰囲気だけでちゃんと調べたことも無かったので、今回軽く調べてみてのメモ書きです。
こんなとこに with
比較的よく使う機能ですが、以下のような機能で with 文が使用されます。
結局、with 文はなんなのか?
Python ドキュメント には次のように記されています。
Python の with 文は、コンテキストマネージャによって定義される実行時コンテキストの概念をサポートします。これは、文の本体が実行される前に進入し文の終わりで脱出する実行時コンテキストを、ユーザ定義クラスが定義できるようにする一対のメソッドで実装されます。
何のこっちゃと思うところではありますが、要は with 文の最初 (enter) と最後 (exit) のタイミングに実行させたい処理をメソッド本体とは切り離して定義できますよ。ということでしょうか。
データベースやファイルなど外部リソース接続時のクローズ処理など、ある程度お決まり、かつ忘れるとよろしくない部分を予め実行されるように仕込んでおく、などの使い方ができます。
サンプル
練習がてら、コンテキストマネージャと、with を用いた処理を書いてみます。
ベーシック
クラスに __enter__
メソッドと __exit__
メソッドを実装します。
__enter__
メソッド
with 文開始時にコールされます。
ここで返す値なりオブジェクトを with 文の as エイリアスで受けることができます(ドキュメントをよく読まず、数分ここでハマった)。
__exit__
メソッド
with 文終了時にコールされます。
exc_type
, exc_value
, traceback
は with 文内で例外が発生した際に例外情報を受取ります。例外が発生しないときはすべて None がセットされます。
また、このメソッドのレスポンスは例外発生時の例外の扱いを指示するものであり、True
を返すと例外の伝播が抑止され、False
を返すと例外が呼び出し元に伝播します。
class Bar:
def __init__(self, s):
self.s = s
def __enter__(self):
print('__enter__ called')
return self # ここ忘れると悲しい
def __exit__(self, exc_type, exc_value, traceback):
print('__exit__ called')
# with 文の中で例外が発生するとこれらの引数にエラー情報がセットされる
# それ以外のときは NoneType
print('exc_type: ' + str(exc_type))
print('exc_value: ' + str(exc_value))
print('traceback: ' + str(traceback))
# True を返すと例外の伝播を抑止する
# False を返すと例外が伝播する
return True
def bar(self):
return 'bar: ' + self.s
if __name__ == '__main__':
with Bar('with dayo') as bar:
print(bar.bar())
これを実行すると、次の様な出力が得られるはずです。
__enter__ called
bar: with dayo
__exit__ called
exc_type: None
exc_value: None
traceback: None
なお、with 文を用いず、単純に Bar
のインスタンスを作成する場合、コンテキストマネージャが働かず、
__enter__
, __exit__
は実行されません。
所感
アプリケーションを構築するうえでリソースの開放漏れというのは非常に厄介です。テストでは気づきにくく、ローンチ後も見かけ上は問題なく動作してしまいます。
問題に気づくのは大抵レスポンスが悪くなっていたり、最悪アプリケーションが停止したり何らかの目に見える被害が発生してからです。こういった不具合はビジネスにも大きな影響を与えることになります。
with 文の様に、言語レベルで予防策を講じることができるのは非常に有用だと思います。