python のコンテキストマネージャーwithはどういう仕組で動いている? __enter__と__exit__メソッドを持ったwith対応クラスを作成する

コンテキストマネージャーであるwithはどういう仕組で動いているのか?

ファイルのオープン・クローズでよく見かけるwith ですが、これがどうやって動いているのかを紹介します。
withはコンテキストマネージャーと呼ばれます。
コンテキスト =   ”文脈 or 状況”
マネージャー = ”管理者”
状況をうまく管理してくれる奴ぐらいの意味です。

まずよく見かけるファイルを開いて中身を取り出す時に使うwith構文

with open('test.txt', 'r') as f:
    print("1:", f.readlines())
    print("2:", f.closed)
print("3:", f.closed)
1: ['text']
2: False
3: True

test.textには'text'という文字列一行からなるtxtファイルです。
f.closedでファイルオブジェクトの開閉を調べています。
withブロック内ではf.closedはFalse(つまりファイルオブジェクトはメモリにのったまま)ですが、ブロックを出るとf.closedはTrueでファイルは閉じられています。

withは、enterexitを使って次のように書き換えることができます。

f = open('test.txt', 'r')
obj = f.__enter__()
try:
    print("1:", f.readlines())
    print("2:", f.closed)
finally:
    print("3:", f.closed)
    obj.__exit__()
    print("4:", f.closed)
1: ['text']
2: False
3: False
4: True

まずobj.enter()でオブジェクトをコンテキストでの管理を開始します。
obj.exit()によってオブジェクトの管理が終了し、リソースから削除されます(f.close()と同じことです)。

コンテキストはいつ使われるか?

  • ファイルの開閉する時
  • 小数点精度を状況によって変更したいような時

状況に応じて(withブロック内でのみ)特殊な操作をしたい時に便利ですね。

コンテキストマネージャーwithが使えるクラスの作成方法

クラスから作成したインスタンスがwithに対応するためには、
そのクラスがenterexitメソッドを持つようにする必要があります。

withブロックのスコープはforループや関数とは異なる

withブロックのスコープはforループや関数とは異なり、その変数はグローバルスコープに属している。

__exit__の振る舞い

try文のfinallyと同じで、withブロックで例外が発生しても、exitは実行されます。

 

 

 

コメント