SQLite3で毎回DBを開閉するデコレータ
sqlite3.ProgrammingError 対策のつもりで実験。
成果物
Python.Sqlite3.decorator.20180228103240
概要
SQLite3でSQL実行時、毎回DBを開閉するデコレータを書いてみた。
作った経緯
マルチスレッドやconnect()
を使い回したとき、エラーになるから。
問題
Pythonでdataset(SQLAlchemy(SQLite3))を使っていると、以下のエラーが出た。
sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread.The object was created in thread id 1972261984 and this is thread id 1995563008
ソースコード
transact.py
def transact(db_url): def transact(func): import functools @functools.wraps(func) def wrapper(*args, **kwargs): with dataset.connect(db_url) as db: db.begin() res = func(db, *args, **kwargs) db.commit() return res return wrapper return transact
run.py
from transact import transact @transact('sqlite:///test.db') def initialize_db(db): db.query('select 1') initialize_db()
実行
$ python3 run.py
課題
db_url
をリテラルでなく変数にしたい- デコレータをクラスで包みたい
開発環境
- Raspberry Pi 3 Model B
疑問
デコレータ引数をつけるならクラスにしたほうが良くね?
デコレータである必要性
そもそも、デコレータである必要性はあるのか? 動的な引数が必要ならクラスにしたほうが良くない? 何が違う? どう使い分ける?
デコレータの使いどころ
たぶん、デコレータは、ちょっとデコりたいときに使うと楽になる、くらいの奴?
デコレータ引数
でも、デコレータに引数を渡したいときは面倒にならないか? コード把握が。
- デコレータ関数内のネストが深い
- 呼出と定義で引数の見え方が違う
- デコレータの実装を見ないと理解不能
- デコレータと被デコレータの引数が、それぞれ異なるスコープになりうる
- 被デコレータの定義と呼出を別ファイルにしたとき
被デコレータ呼出
initialize_db()
を呼び出すとき、ぱっと見、混乱する。その関数定義をみると引数db
が必要にみえるから。デコレータの実装まで見ないと、db
が不要であることも、それがどんなオブジェクトかも分からない。
そういうものだと慣れるしかない? でもクラスならメンバ変数として隠蔽できるのに。呼出側に優しいインターフェースのほうが良い。するとクラスに軍配が上がるのでは?
結論
デコレータ引数をつけるならクラスにしたほうが良くね? そもそも別物だし用途も違うけど。
方法 | 使いどころ |
---|---|
デコレータ | デコレータ引数が不要なときに使うと楽? |
クラス | デコレータ引数が必要なときは、クラスに置き換えたほうが綺麗? |