Help us understand the problem. What is going on with this article?

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

(この記事は私の 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
umezawatakeshi
AVX-512 が実装されたパフォーマンスデスクトップ向けプロセッサを待ち続けて早幾年月
http://umezawa.dyndns.info/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした