6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python+MySQL でコネクションやカーソルと with 文

Last updated at Posted at 2019-08-05

(この記事は私の blog の http://umezawa.dyndns.info/wordpress/?p=7317 の転載です)

また Python+MySQL ネタ。

前置き

カーソルを使ったら閉じないといけません。処理後にその流れでそのまま cursor.close() を呼ぶように書くと例外が飛んだり長い処理の途中でうっかり return したりしたときに呼び漏れが発生するので、tryfinally のような構造を使いたいところです。(Java でもたぶんそう(20年前の知識)。C++ だとデストラクタが使えるので違う書き方になる)

cursor = conn.cursor()
try:
    cursor.execute("SELECT * FROM table")
    rows = cursor.fetchall()
    ...
finally:
    cursor.close()

カーソルと with

ところで、Python には with 文というのがあって、 with 文の中のブロックを(抜け方にかかわらず)抜けたときに一定の終了処理を自動で実行することができます。PyMySQL と mysqlclient ではカーソルに対してこれを使うことができます。

with conn.cursor() as cursor:
    cursor.execute("SELECT * FROM table")
    rows = cursor.fetchall()
    ...
    # with から抜けるときに cursor.close() が自動的に呼ばれる

残念なことに MySQL Connector/Python だとこの書式は使えません。with文は with の後に来るオブジェクトの __enter__ を呼んで with を抜けるときに __exit__ を呼ぶという仕組みなのですが、カーソルにはこれらのマジックメソッドが実装されていないからです。

Traceback (most recent call last):
  File "./test_cursor_with.py", line 29, in <module>
    with conn.cursor() as cur:
AttributeError: __enter__

ただし、 with を抜けるときに close() を呼んでくれるようになる closing というラッパーメソッドが Python 標準の contextlib モジュールの中にあるので、自前でラッパーを書いたりする必要はありません。

from contextlib import closing
with closing(conn.cursor()) as cursor:
    cursor.execute("SELECT * FROM table")
    rows = cursor.fetchall()
    ...

コネクションの場合

さて、カーソルを with で使えるならコネクションでも使ってみようかと思うわけですが、何も考えずに with に渡すとおかしな挙動になります。(commitrollback についてはとりあえず考えないことにします)

with pymysql.connect(**mysql_kwargs) as conn:
    print(conn.__class__.__module__ + "." + conn.__class__.__name__)
pymysql.cursors.Cursor

コネクションが来ることを期待していたのですが、何故かカーソルになって渡ってきます。 mysqlclient でも同じ。なんでやねん。

コネクション型である pymysql.connections.Connection の定義を見てもそもそも __enter__ とかは定義されていないのでエラーになるのが期待されると思うのですが、なんでこんな挙動になるのか分かりません。うーん。

使用環境

  • Ubuntu 18.04 LTS
  • Python 3.6.8
  • PyMySQL 0.8.0
  • MySQL Connector/Python 2.1.6
  • mysqlclient 1.3.10
6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?