我有一个连接到数据库并获取一些数据并返回此数据的函数。除finally块外,整个过程都围绕着尝试。在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 = None
和finally
块的初始化看起来不够优雅或pythonic,我想知道是否有更好的方法可以做到这一点?
答案 0 :(得分:6)
您要使用上下文管理器。上下文管理器和with
语句已添加到专门用于处理这种模式的语言中。
代码将变为:
with DBConnection() as db_conn:
data = db_conn.get_data()
请参见With Statement Context Managers。 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
我在return self
处理程序中使用了__enter__
,因为这是用于上下文管理器的良好模式,非常适合您的特定示例,但是您还可以返回其他内容。例如,某些数据库适配器会在该点返回事务对象或游标。
请注意,在__enter__
中引发的异常不是上下文的一部分,也不会在那里处理!如果在打开不想传播的数据库时需要处理异常,则必须将连接推迟到第一次get_data()
调用之前。
上下文管理器封装 try: ... except ...: finally: ...
模式。另请参见将其添加到Python的PEP 343 -- The *with* statement提案:
此PEP在Python语言中添加了一个新的“ with”语句,从而可以排除try / finally语句的标准用法。
请注意,处理异常的位置取决于您的用例。对于某些使用__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:
# ...
上下文管理器的作用是确保文件对象(如果已打开)处于关闭状态,而没有其他情况。