手动调用enter和exit

2024-05-28 20:50:53 发布

您现在位置:Python中文网/ 问答频道 /正文

我在谷歌上搜索了^{}但没有运气。假设我有一个MySQL连接类,它使用__enter____exit__函数(最初与with语句一起使用)来连接/断开与数据库的连接。

让我们有一个使用其中两个连接的类(例如用于数据同步)。注意:这不是我的真实场景,但它似乎是最简单的例子。

最简单的方法是这样上课:

class DataSync(object):

    def __init__(self):
        self.master_connection = MySQLConnection(param_set_1)
        self.slave_connection = MySQLConnection(param_set_2)

    def __enter__(self):
            self.master_connection.__enter__()
            self.slave_connection.__enter__()
            return self

    def __exit__(self, exc_type, exc, traceback):
            self.master_connection.__exit__(exc_type, exc, traceback)
            self.slave_connection.__exit__(exc_type, exc, traceback)

    # Some real operation functions

# Simple usage example
with DataSync() as sync:
    records = sync.master_connection.fetch_records()
    sync.slave_connection.push_records(records)

Q:这样手动调用__enter__/__exit__可以吗(有什么问题吗)?

Pylint 1.1.0对此没有发出任何警告,我也没有找到任何关于它的文章(Begging中的google链接)。

打个电话怎么样:

try:
    # Db query
except MySQL.ServerDisconnectedException:
    self.master_connection.__exit__(None, None, None)
    self.master_connection.__enter__()
    # Retry

这是好的/坏的做法吗?为什么?


Tags: selfmasternonedeftypewithmysqlexit
3条回答

你的第一个例子不是个好主意:

  1. 如果slave_connection.__enter__引发异常会发生什么情况:

    • master_connection获取其资源
    • slave_connection失败
    • DataSync.__enter__提出异常
    • DataSync.__exit__不运行
    • master_connection从未清理过!
    • 潜在的坏事
  2. 如果master_connection.__exit__抛出异常会发生什么?

    • DataSync.__exit__提前完成
    • slave_connection从未清理过!
    • 潜在的坏事

contextlib.ExitStack可以帮助您:

def __enter__(self):
    with ExitStack() as stack:
        stack.enter_context(self.master_connection)
        stack.enter_context(self.slave_connection)
        self._stack = stack.pop_all()
    return self

def __exit__(self, exc_type, exc, traceback):
    self._stack.__exit__(self, exc_type, exc, traceback)

问同样的问题:

  1. 如果slave_connection.__enter__引发异常会发生什么情况:

    • with块被退出,并且stack清除master_connection
    • 一切都好!
  2. 如果master_connection.__exit__抛出异常会发生什么?

    • 没关系,slave_connection在调用之前被清除
    • 一切都好!
  3. 好吧,如果slave_connection.__exit__抛出异常会怎么样?

    • ExitStack确保调用master_connection.__exit__无论从属连接发生什么情况
    • 一切都好!

直接调用__enter__没有问题,但是如果需要在多个对象上调用它,请确保正确清理!

我只响应头,即从IPython提示符调用__enter____exit__。你可以这样做:


ctx.__enter__()
ctx.__exit__(*sys.exc_info())

注意:当对基础方法__enter____exit__有多个调用时,此答案不能正确解释可能的失败。看到埃里克的答案,就知道他是谁。

不,这没什么问题。甚至在标准库中也有这样做的地方。就像^{} module

class SemLock(object):

    def __init__(self, kind, value, maxvalue, *, ctx):
            ...
            try:
                sl = self._semlock = _multiprocessing.SemLock(
                    kind, value, maxvalue, self._make_name(),
                    unlink_now)
            except FileExistsError:
                pass
    ...

    def __enter__(self):
        return self._semlock.__enter__()

    def __exit__(self, *args):
        return self._semlock.__exit__(*args)

或者^{} module

class _TemporaryFileWrapper:

    def __init__(self, file, name, delete=True):
        self.file = file
        self.name = name
        self.delete = delete
        self._closer = _TemporaryFileCloser(file, name, delete)

    ...

    # The underlying __enter__ method returns the wrong object
    # (self.file) so override it to return the wrapper
    def __enter__(self):
        self.file.__enter__()
        return self

    # Need to trap __exit__ as well to ensure the file gets
    # deleted when used in a with statement
    def __exit__(self, exc, value, tb):
        result = self.file.__exit__(exc, value, tb)
        self.close()
        return result

标准库示例不会为两个对象调用__enter__/__exit__,但是如果您有一个对象负责为多个对象而不是一个对象创建/销毁上下文,那么为所有对象调用__enter__/__exit__就可以了。

唯一可能的问题是正确地处理对您正在管理的对象的__enter____exit__调用的返回值。对于__enter__,您需要确保返回的是包装器对象的用户从with ... as <state>:调用返回所需的state。对于__exit__,您需要决定是传播上下文中发生的任何异常(通过返回False),还是抑制它(通过返回True)。您的托管对象可以尝试以任何一种方式执行此操作,您需要决定包装器对象的意义。

相关问题 更多 >

    热门问题