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

nit*_*red 2 python refactoring

我有一个连接到数据库并获取一些数据并返回此数据的函数.除了finally块之外,整个过程都被try包围.在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()
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ers 6

您想使用上下文管理器.with语言管理器和语句被专门添加到语言中以处理此模式.

代码将变为:

with DBConnection() as db_conn:
    data = db_conn.get_data()
Run Code Online (Sandbox Code Playgroud)

请参阅语句上下文管理器.的实施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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

请注意,由您处理异常的位置取决于您的用例.对于某些上下文管理器而言__exit__,对于其他人来说,只有清理上下文而不是抑制异常才有用.例如,文件是上下文管理器,但它们不会清除异常.那时你要添加一个try: .. except SpecificException:来处理这种情况:

try:
    with open(filename) as fobj:
        # ...
except IOError:
    # oops, file failed to open
Run Code Online (Sandbox Code Playgroud)

要么

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

上下文管理器的目的是确保文件对象在打开时关闭,而不是其他任何东西.