Python の with 文のサンプルは、ネット検索するとたくさんあります。
クラスを用意、__enter__ と __exit__ で前処理、後処理を実行する方法
# -*- coding: UTF-8 -*- 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())) # print('%d' % 'a')
この例では、、
__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()