python の with って何に使うのかずっとわからなかった。やっと意味がわかったのでメモ。
with って便利なの?
この神記事にすべてがありました
database 接続 / file開く など、何かしら connection close などの後始末が必要なときは with が最高に便利です!
何がいいの?
コードがシンプルになります
コードがきれいになる例
残念な例
try:
obj = SomeClass()
obj.do_something()
finally:
obj.close()
5行あります。
Cool な例
with SomeClass() as obj:
obj.do_something()
以上! 2行で終わりました! ちゃんとclose()処理はされてますよ :)
with で使える class とは
最初にいっとくと、関数では with ができません。必ず class にしましょう。
関数 → class への変換方法
# 関数の宣言
def some(arg1):
return do_something(arg1)
# 関数使い方
some('arg1')
# classの宣言
class SomeClass:
def __init__(self, arg1):
do_something(arg1)
# class 使い方
SomeClass('arg1')
with で使える class の作り方
↑ の例のように、普通に class 作るだけだとwithで使えません。
__enter__ または __exit__
がないぞ、的なエラーが出ます。
class W:
def __init__(self):
print('init')
with W() as w:
print(w)
result
AttributeError: __enter__
これは with句で呼び出されたclassは、暗黙的に __enter__() __exit__()
を呼び出されるからです。エラーを消すには、この関数を作ってあげればよいです。
withで使わない場合
class W:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self, exception_type, exception_value, traceback):
print('exit')
w = W()
print('===')
result
init
===
withで使う場合
class W:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self, exception_type, exception_value, traceback):
print('exit')
with W() as w:
print(w)
print('===')
result
init
enter
<__main__.W object at 0x10347bfd0>
exit
===
悩んだエラー TypeError: __exit__()
__exit__()
の引数をちゃんとしないと、エラー
TypeError:
__exit__()
takes 1 positional argument but 4 were given
# wrong
def __exit__(self):
# right
def __exit__(self, exception_type, exception_value, traceback):
with class をインスタンスで返す関数の場合
問題なく動く
class W:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self, exception_type, exception_value, traceback):
print('exit')
def func():
return W()
with func() as w:
print(w)
result
init
enter
<__main__.W object at 0x10bddbfd0>
exit
with class を class のままで返す関数の場合
無茶苦茶だけど動く
class W:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self, exception_type, exception_value, traceback):
print('exit')
def get_class():
return W
with get_class()() as w: #<--- ()() ってひどい.顔文字か。
print(w)
result
init
enter
<__main__.W object at 0x10bdc0fd0>
exit
summary
__enter__, __exit__
は with で使ったときだ だけ 呼び出される
__exit__()
は必ず4つ引数を受け取る