Python教我们使用__enter__
和__exit__
来清理对象。如果我需要创建一个对象,而该对象必须使用上下文管理器,那该怎么办?想象一下:
from database1 import DB1
from database2 import DB2
通常情况下,它们的用法如下:
with DB1() as db1, DB2() as db2:
db1.do_stuff()
db2.do_other_stuff()
无论发生什么,db1
和db2
都将运行它们的__exit__
函数,并清理连接、刷新等。
当我把所有这些都放到一个类中时,我该怎么做呢?这是对的吗?这显然是不对的,db1
和db2
的上下文管理器运行在块的末尾,正如注解中指出的那样。
class MyApp(object):
def __enter__(self):
with DB1() as self.db1, DB2() as self.db2:
return self
def __exit__(self, type, value, traceback):
self.db1.__exit__(self, type, value, traceback)
self.db2.__exit__(self, type, value, traceback)
我甚至考虑过这样做:这看起来是个好主意,实际上(经过一些清理):
class MyApp(object):
def __init__(self):
self.db1 = DB1()
self.db2 = DB2()
def __enter__(self):
self.db1.__enter__()
self.db2.__enter__()
return self
def __exit__(self, type, value, traceback):
try:
self.db1.__exit__(self, type, value, traceback)
except:
pass
try:
self.db2.__exit__(self, type, value, traceback)
except:
pass
编辑:修复代码。
3条答案
按热度按时间wmtdaxz31#
我会使用第二种解决方案,但也会处理数据库错误:
第一个函数在
__enter__
中调用__exit__
,因为with
-所以,不起作用。**编辑:**还可以查看answer by @Ming。在许多情况下,它更短。
sbtkgmzw2#
大多数上下文管理器都可以使用
@contextmanager
装饰器来编写。您可以编写一个带有一个yield的函数,在yield之前是您的“enter”函数,在yield之后是您的“exit”函数。由于生成器的实现方式,如果yield在with语句中,则with语句不会在yield处退出。例如
产出:
mrphzbgm3#
这取决于你想要达到的总体目标。一种可能性是构建单独的上下文管理器,然后将它们与标准库的contextlib.nested结合起来。这将为你提供一个单一的对象,它的行为类似于你的示例
MyApp
,但以DRY(不要重复自己)的方式利用现有的标准库。