当必须释放连接时,使用 try/except/finally 的 pythonic 方法是什么?



我有一个函数,它连接到数据库并获取一些数据并返回这些数据。整个过程被尝试包围,但最终阻止。在 finally 块中,如果资源存在,即使发生错误,我也会释放它们。那么什么是pythonic方法可以执行以下操作:

def fetch_data():
db_conn = None
try:
db_conn = DBConnection()
data = db_conn.get_data()
except Exception as ex:
print(f"Exception occurred when fetching data: {ex}")
finally:
if db_conn:
db_conn.close()

db_conn = Nonefinally块的初始化看起来不够优雅或 pythonic,我想知道是否有更好的方法可以做到这一点?

你想要使用上下文管理器。上下文管理器和with语句被添加到语言中,专门用于处理此模式。

代码将变为:

with DBConnection() as db_conn:
data = db_conn.get_data()

请参阅使用语句上下文管理器DBConnection的实现需要提供__enter____exit__方法来处理此问题:

class DBConnection:
def __init__(self):
# .. initialisation of the instance. **Not** in the context manager
def close(self):
# ...
def get_data(self):
# ...
return data_loaded
def __enter__(self):
# context is entered, `try:` 'opens' *after* this point.
# Perhaps you want to actually connect to the database here
# whatever is returned here is assignable via `with ... as name`
# this can be a new object or self
return self
def __exit__(self, exc_type, exc_value, traceback):
# The context is exiting, equivalent of except ... and finally ...
self.close()
if exc_type:
print(f"Exception occurred when fetching data: {exc_value}")
# returning None will allow exceptions to propagate, returning
# True (or other true value) will clear the exception.
return True  # exception is cleared

我在__enter__处理程序中使用了return self,因为这是一个很好的模式,可用于上下文管理器,非常适合您的特定示例,但您也可以返回其他内容。例如,某些数据库适配器在该点返回事务对象或游标。

请注意,__enter__中引发的异常不是上下文的一部分,不会在那里处理!如果需要处理打开不想传播的数据库时的异常,则必须将连接推迟到第一次get_data()调用。

上下文管理器封装try: ... except ...: finally: ...模式。另请参阅 PEP 343 -- 将其添加到 Python 中的 *with* 语句提案:

这个PEP在Python语言中添加了一个"with"的新语句,以便可以分解try/final语句的标准用法。

请注意,这取决于您的用例,以决定在何处处理异常。对于某些上下文管理器,这是__exit__方法,对于其他上下文管理器,仅清理上下文而不抑制异常才有用。例如,文件是上下文管理器,但它们不会清除异常。此时,您将添加一个try: .. except SpecificException:来处理此类情况:

try:
with open(filename) as fobj:
# ...
except IOError:
# oops, file failed to open

try:
open_file = open(filename)
except IOError:
# oops, file failed to open
else:
with open_file as fobj:
# ...

上下文管理器的要点是确保文件对象在已打开时已关闭,而不是其他任何内容。

最新更新