__enter__ 和 __exit__ 是 Python 数据库 API 中指定的连接对象的行为吗? [英] Is __enter__ and __exit__ behaviour for connection objects specified in the Python database API?

查看:35
本文介绍了__enter__ 和 __exit__ 是 Python 数据库 API 中指定的连接对象的行为吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

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

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

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

<预><代码>>>>使用 util.get_db_connection() 作为连接:...打印连接...<MySQLdb.cursors.Cursor 对象在 0xb6ca8b4c>

get_db_connection() 返回一个 MySQLdb 连接对象,但该连接对象的 __enter__ 方法返回一个游标对象,而不是我期望的连接对象本身__enter____exit__ 适用于文件对象.我想我应该使用 util.get_db_connection() 作为游标:,或者根本不使用 with.

问题

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

  1. MySQLdb 连接对象的 __enter____exit__ 方法还有什么作用?__exit__ 是否会在没有我明确要求发生的情况下神奇地为我提交或回滚更改?还有什么我应该知道的非显而易见的事情吗?
  2. 这种行为在 Python 数据库 API 的其他实现者(如 sqlite3、django 或 psycopg2)中是否相同?
  3. 这种行为是否正式规定在任何地方?ctrl-f 使用最新规范(PEP 249 -- Python用于输入"、退出"和上下文管理器"的数据库 API 规范 v2.0) 不会抛出任何内容.

解决方案

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

因此,不同的数据库库就如何实现上下文管理器支持做出了自己的决定(如果它们完全实现了).

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

connection = util.get_db_connection()以连接为游标:游标.执行(...)# 如果没有引发异常,则发出连接提交.

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

con = sqlite3.connect(":memory:")与骗局:游标 = con.cursor()# 或者直接使用连接执行(...)

从技术上讲,它只是在 __enter__ 上返回 self.

Background

I recently discovered the Python with keyword and started seeing its potential usefulness for more prettily handling some scenarios where I'd previously have used try: ... finally: ... constructs. I immediately decided to try it out on the MySQLdb connection object in some code I was writing.

I didn't bother reading up on how __enter__ and __exit__ behave in implementors of the Python database API, and naively expected the behaviour to be like that of file objects - all I was expecting was for exit to call connection.close().

Imagine my confusion, then, at this behaviour:

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

get_db_connection() returns a MySQLdb connection object, but the __enter__ method of that connection object returns a cursor object, not the connection object itself like I was expecting given how __enter__ and __exit__ work for file objects. I guess I ought to be doing with util.get_db_connection() as cursor:, or else not using with at all.

Questions

Immediately this discovery makes me wonder a few things:

  1. What else do the __enter__ and __exit__ methods of MySQLdb connection objects do? Is __exit__ going to magically commit or rollback changes for me without me explicitly asking for that to happen? Is there anything else non-obvious that I should know?
  2. Is this behaviour the same in other implementers of the Python database API (like sqlite3, django, or psycopg2)?
  3. Is this behaviour formally specced anywhere? ctrl-fing the latest spec (PEP 249 -- Python Database API Specification v2.0) for 'enter', 'exit' and 'context manager' doesn't throw up anything.

解决方案

The Python DBAPI was written well before context managers were added to the Python language.

As such, different database libraries made their own decisions on how to implement context manager support (if they implemented it at all).

Usually using the database as a context manager ties you to a transaction. The transaction is started on __enter__, and committed or aborted on __exit__, depending on wether or not there was an exception. As such, you are supposed to use the MySQL connection as a context manager after connecting seperately:

connection = util.get_db_connection()

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

# connection commit is issued if no exceptions were raised.

The sqlite3 context manager implementation is subtly different; it also manages transactions, but does not return a cursor from the __enter__ method:

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

Technically, it just returns self on __enter__.

这篇关于__enter__ 和 __exit__ 是 Python 数据库 API 中指定的连接对象的行为吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆