如何将子上下文的管理委托给父上下文
def __enter__()
def __exit__()
假设我们有一个叫做'A'的类,它本身就是一个上下文管理器,所以它实现了相应的接口。这样,客户端代码可以直接使用with
语句来创建'A'对象。
现在,我们还有另一个类'B',它封装了其他功能,同时也使用了'A'对象。
如果我们想让'B'也作为一个上下文管理器,那么管理它的'A'实例的正确方法是什么呢?
在'B'的__enter__
和__exit__
方法中,应该调用它的'A'对象的__enter__
和__exit__
方法吗?还是有更好的方法呢?
为了给出一个具体的例子(这并不是我在应用中使用的,只是我想到的第一个非抽象的例子),我们考虑两个类:
DatabaseConnection
(数据库连接)DatabaseConnectionPool
(数据库连接池)
单独使用一个DatabaseConnection
是有效的,因此DatabaseConnection
实现了上下文管理器接口。
DatabaseConnectionPool
使用了多个DatabaseConnection
以及其他一些东西。使用DatabaseConnectionPool
(也就是"with"语句)应该能够对它的DatabaseConnection
实例进行设置和清理(以及它可能想做的其他事情)。
更新:我写了一些测试代码,希望能得到以下输出:
Enter invoked on Outer Enter invoked on Inner Within outer context... do_foo invoked! Still using outer... Exit invoked on inner Exit invoked on outer Done using outer
但我得到了以下结果:
Enter invoked on Outer Enter invoked on Inner Exit invoked on inner Within outer context... do_foo invoked! Still using outer... Exit invoked on outer Done using outer
代码:
class Inner(object):
def __enter__(self):
print "Enter invoked on Inner"
return self
def __exit__(self, typ, val, tb):
print "Exit invoked on inner"
def do_foo(self):
print "do_foo invoked!"
class Outer(object):
def __init__(self):
self._inner = Inner()
def __enter__(self):
print "Enter invoked on Outer"
with self._inner as ctx:
return self
def __exit__(self, typ, val, tb):
print "Exit invoked on outer"
with Outer() as outer:
print "Within outer context..."
outer._inner.do_foo()
print "Still using outer..."
print "Done using outer"
有没有什么想法可以让这个工作正常?
2 个回答
这其实是一个设计方面的问题,需要做一些判断。我建议你考虑以下几点:
一个连接池的类实例可以提供一个上下文,这样程序就可以根据需要使用数据库连接实例。不过,这样做会比简单地对每个数据库实例调用 __enter__
和 __exit__
要复杂一些,我觉得你不应该尝试这样做,因为它们并不是为了这个目的设计的。在这种情况下,我建议你直接使用数据库实例的上下文管理器。
如果你想使用它们的上下文管理器,可以参考下面的写法:
def __enter__(self):
for db in self.pool:
with db as d:
d.transaction()
def __exit__(self, type, value, traceback):
pass
但是如果你想自己处理错误,可以参考下面的写法:
def __enter__(self):
for db in self.pool:
try:
db.connection()
# ... write and transact here
def __exit__(self, type, value, traceback):
if type is None: # no errors, so close as normal
for db in self.pool:
db.close()
# ... more code here for proper error handling
顺便说一下,我也遇到过类似的设计问题,所以我在我的“子”类里写了退出和进入的函数,而“父”类则只作为一个上下文管理器。
(如果我理解你的问题没错的话,你是在问‘我怎么才能优雅地让所有在上下文管理器类中使用的类在关闭父上下文管理器时也能正确关闭’)