12

背景

我最近发现了 Pythonwith关键字,并开始看到它在更漂亮地处理一些我以前使用过try: ... finally: ...构造的场景方面的潜在用途。我立即决定在我正在编写的一些代码中对 MySQLdb 连接对象进行尝试。

我没有费心去阅读Python 数据库 API 的实现者的行为方式__enter____exit__行为方式,并且天真地期望这种行为类似于文件对象的行为——我所期望的只是 exit 调用connection.close().

那么,想象一下我对这种行为的困惑:

>>> with util.get_db_connection() as conn:
...     print conn
... 
<MySQLdb.cursors.Cursor object at 0xb6ca8b4c>

get_db_connection()返回一个 MySQLdb 连接对象,但该__enter__连接对象的方法返回一个游标对象,而不是我所期望的连接对象本身,因为文件对象的方式__enter____exit__工作方式。我想我应该这样做with util.get_db_connection() as cursor:,否则根本不使用with

问题

这个发现立刻让我想知道一些事情:

  1. __enter__MySQLdb 连接对象的和方法还有什么__exit__作用?是否__exit__会在没有我明确要求的情况下为我神奇地提交或回滚更改?还有什么我应该知道的不明显的吗?
  2. 这种行为在 Python 数据库 API 的其他实现者(如 sqlite3、django 或 psycopg2)中是否相同?
  3. 这种行为在任何地方都有正式规定吗?ctrl-f为 'enter'、'exit' 和 'context manager' 制定最新规范(PEP 249 -- Python Database API Specification v2.0)并没有抛出任何东西。
4

2 回答 2

20

Python DBAPI 在上下文管理器被添加到 Python 语言之前就已经编写好了。

因此,不同的数据库库自己决定如何实现上下文管理器支持(如果他们实现了它的话)。

通常将数据库用作上下文管理器会将您与事务联系起来。事务开始于__enter__,并于 提交或中止__exit__,具体取决于是否存在异常。因此,您应该在单独连接后使用 MySQL 连接作为上下文管理器:

connection = util.get_db_connection()

with connection as cursor:
    cursor.execute(...)

# connection commit is issued if no exceptions were raised.

sqlite3上下文管理器的实现略有不同;它还管理事务,但不从__enter__方法返回游标:

con = sqlite3.connect(":memory:")
with con:
    cursor = con.cursor()
    # or use the connection directly
    con.execute(...)

从技术上讲,它只是返回self__enter__

于 2013-03-22T10:38:43.977 回答
-1

请参阅__enter__此链接中的功能。 https://github.com/PyMySQL/mysqlclient-python/blob/master/MySQLdb/connections.py

__enter__连接对象的函数返回self.cursor()

这就是为什么你得到游标对象而不是 Connection 对象的原因。

于 2017-04-14T11:05:55.133 回答