Python の with 文のサンプルは、ネット検索するとたくさんあります。
クラスを用意、__enter__ と __exit__ で前処理、後処理を実行する方法
class Foo():
def __init__(self, msg):
self.msg = msg
def getMsg(self):
return self.msg
def __enter__(self):
print('Foo __enter__')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('Foo __exit__')
if exc_type:
print("exc_type : %s" % exc_type)
print("exc_val : %s" % exc_val)
print("exc_tb : %s" % exc_tb)
with Foo('Hello') as f:
print('test {}'.format(f.getMsg()))
この例では、、
__enter__で、return self により自身のインスタンスを返して
メソッド getMsg() が実行できるようにします。
結果
Foo __enter__
test Hello
Foo __exit__
これに、例外を発生するように実行します。
with Foo('Hello') as f:
print('test {}'.format(f.getMsg()))
print('%d' % 'a')
例外を発生さえた結果
Foo __enter__
Traceback (most recent call last):
test Hello
Foo __exit__
exc_type : <class 'TypeError'>
exc_val : %d format: a number is required, not str
exc_tb : <traceback object at 0x0362FB70>
contextmanagerを使う方法
これは実行するメソッドがインターセプト対象になり、
前処理、後処理を、yeild 前後で書くことになります。
例外は、try:~except で捕捉する処理を書きます。
from contextlib import contextmanager
from traceback import format_exception
@contextmanager
def execute(msg):
try:
print('# 前処理')
yield msg.upper()
print('# 後処理')
except BaseException as e:
print(''.join(format_exception(type(e), e, e.__traceback__)))
finally:
print('# finally')
with execute('Hello') as e:
print('-- start')
print(e)
print('-- end')
yield で、値を返すこともできます。
結果
# 前処理
-- start
HELLO
-- end
# 後処理
# finally
これに、例外を発生するように実行します。
with execute('Hello') as e:
print('-- start')
print(e)
print('%d' % 'a')
例外を発生さえた結果
# 前処理
-- start
HELLO
Traceback (most recent call last):
File "C:/Users/xxx/PycharmProjects/xxxx/test.py", line 9, in execute
yield msg.upper()
File "C:/Users/xxx/PycharmProjects/xxx/test.py", line 18, in <module>
print('%d' % 'a')
TypeError: %d format: a number is required, not str
# finally
例外捕捉を以下のように書きましたが、
except BaseException as e:
print(''.join(format_exception(type(e), e, e.__traceback__)))
次のように書いても同じです。
except BaseException as e:
for v in format_exception(type(e), e, e.__traceback__):
print(v.rstrip())
この contextmanager の方法を踏まえて、過去に書いた
oboe2uran.hatenablog.com
に当てはめて書くと、、、
from contextlib import contextmanager
from traceback import format_exception
@contextmanager
def query(statement):
try:
import mysql.connector
connect = mysql.connector.connect(host='localhost', port=3306, user='food', password='a1B01a', database='foodb', charset="utf8")
cur = connect.cursor()
cur.execute(statement)
table = cur.fetchall()
yield table
except BaseException as e:
print(''.join(format_exception(type(e), e, e.__traceback__)))
finally:
connect.close()
with query('SELECT * FROM users') as res:
print(res)
で、結果 res は、1レコードはタプルでSELECT全行の結果としてリストで返ってきます。
クエリメソッドではなく、コネクション接続後のカーソルにする場合は、、
from contextlib import contextmanager
from traceback import format_exception
@contextmanager
def connect():
try:
import mysql.connector
connect = mysql.connector.connect(host='localhost', port=3306, user='food', password='a1B01a', database='foodb', charset="utf8")
cur = connect.cursor()
yield cur
except BaseException as e:
print(''.join(format_exception(type(e), e, e.__traceback__)))
finally:
connect.close()
with connect() as cur:
cur.execute("SELECT * FROM users")
table = cur.fetchall()
print(table)
トランザクションの為に書けば、、
commit と roleback を差し込んで以下のように書けますね。
from contextlib import contextmanager
from traceback import format_exception
@contextmanager
def connect():
try:
import mysql.connector
connect = mysql.connector.connect(host='localhost', port=3306, user='food', password='a1B01a', database='foodb', charset="utf8")
cur = connect.cursor()
yield cur
connect.commit
except BaseException as e:
connect.rollback()
print(''.join(format_exception(type(e), e, e.__traceback__)))
finally:
connect.close()