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 方法中使用?
- 我认为,
ExitStack
会让您的代码更简单
- IMO,明确地使用
__enter__
和 __exit__
比 next(...)
thingy 更具可读性
- 实际上与 CM 无关,但 惯用语 python 代码的很大一部分包括命名。
opened
和 closed
读取为不应调用 return 布尔值的属性,即 r.opened -> True
。这是大多数人对您的界面的期望。另一方面,Actions 应该拼写为动词,例如 open
和 close
.
包含上述想法的简单示例:
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()
构建对象以打开多个(上下文管理的)资源并使用这些资源的最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 方法中使用?
- 我认为,
ExitStack
会让您的代码更简单 - IMO,明确地使用
__enter__
和__exit__
比next(...)
thingy 更具可读性
- 实际上与 CM 无关,但 惯用语 python 代码的很大一部分包括命名。
opened
和closed
读取为不应调用 return 布尔值的属性,即r.opened -> True
。这是大多数人对您的界面的期望。另一方面,Actions 应该拼写为动词,例如open
和close
.
包含上述想法的简单示例:
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()