やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

SQLite3で毎回DBを開閉するデコレータ

sqlite3.ProgrammingError 対策のつもりで実験。

成果物

GitHubPython.Sqlite3.decorator.20180228103240

概要

SQLite3でSQL実行時、毎回DBを開閉するデコレータを書いてみた。

作った経緯

マルチスレッドやconnect()を使い回したとき、エラーになるから。

問題

Pythondataset(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リテラルでなく変数にしたい
  • デコレータをクラスで包みたい

開発環境

疑問

デコレータ引数をつけるならクラスにしたほうが良くね?

デコレータである必要性

そもそも、デコレータである必要性はあるのか? 動的な引数が必要ならクラスにしたほうが良くない? 何が違う? どう使い分ける?

デコレータの使いどころ

たぶん、デコレータは、ちょっとデコりたいときに使うと楽になる、くらいの奴?

デコレータ引数

でも、デコレータに引数を渡したいときは面倒にならないか? コード把握が。

  • デコレータ関数内のネストが深い
  • 呼出と定義で引数の見え方が違う
    • デコレータの実装を見ないと理解不能
  • デコレータと被デコレータの引数が、それぞれ異なるスコープになりうる
    • 被デコレータの定義と呼出を別ファイルにしたとき

被デコレータ呼出

initialize_db()を呼び出すとき、ぱっと見、混乱する。その関数定義をみると引数dbが必要にみえるから。デコレータの実装まで見ないと、dbが不要であることも、それがどんなオブジェクトかも分からない。

そういうものだと慣れるしかない? でもクラスならメンバ変数として隠蔽できるのに。呼出側に優しいインターフェースのほうが良い。するとクラスに軍配が上がるのでは?

結論

デコレータ引数をつけるならクラスにしたほうが良くね? そもそも別物だし用途も違うけど。

方法 使いどころ
デコレータ デコレータ引数が不要なときに使うと楽?
クラス デコレータ引数が必要なときは、クラスに置き換えたほうが綺麗?

参考