如何将子上下文的管理委托给父级

edd*_*uld 7 python contextmanager

假设我们有一个类“A”,它本身就充当上下文管理器,因此它实现了

def __enter__()
def __exit__()
Run Code Online (Sandbox Code Playgroud)

界面。客户端代码直接使用with语句创建“A”对象是有效的。

现在,我们还有另一个类“B”,它封装了其他功能,并使用了“A”对象。

如果我们还想让“B”充当上下文管理器,那么管理它的“A”实例的正确方法是什么?

如若实施__enter__,并__exit__于“B”的呼叫__enter__,并__exit__在其上的一个对象实例(分别)?或者,还有更好的方法?

举一个具体的例子(这不是我在我的应用程序中使用的,这只是我想到的第一个非抽象例子)考虑两个类

  • DatabaseConnection
  • DatabaseConnectionPool

单独使用单个是有效DatabaseConnection的,因此DatabaseConnection实现了上下文管理器接口。

DatabaseConnectionPool使用几个DatabaseConnections以及其他位和鲍勃。使用DatabaseConnectionPool(即“with”)应该对它的DatabaseConnection实例进行设置和拆除(以及它可能想做的任何其他事情)

更新: 我写了一些测试代码,我希望能给出以下输出:

在外部调用输入
在内部调用输入
在外部环境中...
do_foo 调用了!
还在用外...
在内部调用退出
在外部调用退出
使用外部完成

但我得到了以下信息:

在外部调用输入
在内部调用输入
在内部调用退出
在外部环境中...
do_foo 调用了!
还在用外...
在外部调用退出
使用外部完成

代码:


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"
Run Code Online (Sandbox Code Playgroud)

关于如何使这项工作的任何想法?

Aar*_*all 0

这实际上是一个设计问题和一个判断。我将提出以下建议:

Pool 类实例提供一个上下文,然后根据需要以编程方式使用数据库连接实例。__enter__但这比简单地为每个数据库实例调用and稍微复杂一些__exit__,我认为您不应该尝试这样做,它们并不是用于此目的。在这种情况下,我建议直接使用数据库实例的上下文管理器。

如果你想使用他们的上下文管理器,像这样的东西会起作用:

def __enter__(self):
    for db in self.pool:
        with db as d:
            d.transaction()

def __exit__(self, type, value, traceback):
    pass 
Run Code Online (Sandbox Code Playgroud)

但如果你想自己处理错误:

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
Run Code Online (Sandbox Code Playgroud)