Python 如何从对象方法惯用地打开多个托管资源

How to idiomatically open multiple managed resources from an object method in Python

构建对象以打开多个(上下文管理的)资源并使用这些资源的最Pythonic 方法是什么?

我有一个 class 打开几个托管资源,然后在 class 方法中操作这些资源。

例如,如果我必须同时打开到本地缓存数据库和 Web 服务器的连接(例如,首先检查缓存中的数据,如果没有则从服务器中拉取)。

我已经能够想出一些代码来使用 yield 语句来管理资源,但它似乎不是很直观。在 Python 中,是否有解决此问题的规范方法?

最小示例:

from contextlib import contextmanager

@contextmanager
def resource_A():
    print('opening resource A...')
    a = 'resource_A'
    yield a
    print('closing resource A...')

@contextmanager
def resource_B():
    print('opening resource B...')
    b = 'resource_B'
    yield b
    print('closing resource B...')

class ResourceManager(object):
    def opened(self):
        self.resources = self._resources()
        self.a, self.b = next(self.resources)
    def closed(self):
        try:
            next(self.resources)
        except StopIteration:
            del self.resources
            del self.a
            del self.b
    def _resources(self):
        with resource_A() as a, resource_B() as b:
            yield a, b
    def processing(self):
        print('doing something with resource_A and resource_B...')
    def __enter__(self):
        self.opened()
        return self
    def __exit__(self, ex_type, ex_val, traceback):
        self.closed()

打开和关闭

>>> r = ResourceManager()
>>> r.opened()
opening resource A...
opening resource B...
>>> r.a
'resource_A'
>>> r.b
'resource_B'
>>> r.closed()
closing resource B...
closing resource A...

与上下文管理器一起使用

>>> with ResourceManager() as r:
...     r.processing()
...
opening resource A...
opening resource B...
doing something with resource_A and resource_B...
closing resource B...
closing resource A...

上面的代码似乎工作正常,但似乎不是很直观。具体来说,yield-next 习语似乎有点难以消化。

是否有更好的方法来 open/close 多个托管资源,这些资源随后将在 Python 中的 class 方法中使用?

  1. 我认为,ExitStack 会让您的代码更简单
  2. IMO,明确地使用 __enter____exit__next(...) thingy
  3. 更具可读性
  4. 实际上与 CM 无关,但 惯用语 python 代码的很大一部分包括命名。 openedclosed 读取为不应调用 return 布尔值的属性,即 r.opened -> True。这是大多数人对您的界面的期望。另一方面,Actions 应该拼写为动词,例如 openclose.

包含上述想法的简单示例:

class ResourceManager(object):
    def open(self):
        self.stack = ExitStack()
        self.a = self.stack.enter_context(resource_A())
        self.b = self.stack.enter_context(resource_B())
    def close(self):
        self.stack.close()

    def processing(self):
        print('doing something with resource_A and resource_B...')
    def __enter__(self):
        self.open()
        return self
    def __exit__(self, ex_type, ex_val, traceback):
        self.close()