処理を終える際に実行すべき処理を書く構文が多くの言語にある。
Java なら try with resources。
C# なら using ステートメント。
Python には with ステートメントがある。
with ステートメントに供するクラスは2つのメソッド __enter__
と __exit__
を用意する必要がある。
with + コンストラクタ
class WithClass:
def __init__(self):
print("init")
def hoge(self):
print("hoge")
def __enter__(self):
print("enter")
return self
def __exit__(self, ex_type, ex_value, trace):
print("exit: ", ex_type, ex_value, trace)
with WithClass() as w:
w.hoge()
こんな感じになる。
実行すると
init
enter
hoge
exit: None None None
と出力される。
私が最初気づかなかったのは、 __enter__(self)
の返戻値が w
の値になるという点。
だから、 return self
は必須。
with + 非コンストラクタ
もうひとつのパターンは、with open(filename) as f:
のように、コンストラクタではないメソッドがある場合。
class WithClass:
def __init__(self):
print("init")
def hoge(self):
print("hoge")
def __enter__(self):
print("enter")
return self
def __exit__(self, ex_type, ex_value, trace):
print("exit: ", ex_type, ex_value, trace)
def creator():
return WithClass()
with creator() as w:
w.hoge()
こんな具合。
このパターンでも __enter__(self)
は必須。
w
の値は __enter__(self)
の返戻値になる。 create()
の返戻値ではない。
というわけで、出力されるのは
init
enter
hoge
exit: None None None
と、最初の例と同じになる。
__enter__(self)
の返戻値を self
以外にしたくなる局面が思いつかないんだけど、きっとなにかあるんだろう。
例外が起きる場合
で。例外を起こしてみる:
class WithClass:
def __init__(self):
print("init")
def hoge(self):
print(1/0) # ゼロ除算!
def __enter__(self):
print("enter")
return self
def __exit__(self, ex_type, ex_value, trace):
print("exit: ", ex_type, ex_value, trace)
print("---")
with WithClass() as w:
w.hoge()
すると、出力はこんな風に
init
enter
exit: <class 'ZeroDivisionError'> division by zero <traceback object at 0x1092539c8>
---
Traceback (most recent call last):
File "with3.py", line 16, in <module>
w.hoge()
File "with3.py", line 6, in hoge
print(1/0)
ZeroDivisionError: division by zero
なる。
ここで、 __exit__(self,略)
で True
を返すと例外を握りつぶしてくれる。
こんな具合:
class WithClass:
def __init__(self):
print("init")
def hoge(self):
print(1/0) # ゼロ除算!
def __enter__(self):
print("enter")
return self
def __exit__(self, ex_type, ex_value, trace):
print("exit: ", ex_type, ex_value, trace)
print("---")
return True
with WithClass() as w:
w.hoge()
出力は
init
enter
exit: <class 'ZeroDivisionError'> division by zero <traceback object at 0x10885e9c8>
---
となっていて、例外が握りつぶされていることがわかる。
参考サイト:
http://shin.hateblo.jp/entry/2013/03/23/211750
http://reiki4040.hatenablog.com/entry/20130331/1364723288