Vyk*_*tor 22 python contextmanager python-3.x
我用谷歌搜索calling __enter__ manually但没有运气.因此,让我们假设我有MySQL连接器类,它使用__enter__和__exit__函数(最初与with语句一起使用)来连接/断开数据库.
让我们有一个使用其中2个连接的类(例如用于数据同步).注意:这不是我现实生活中的情景,但它似乎是最简单的例子.
使这一切协同工作的最简单方法是这样的类:
class DataSync(object):
def __init__(self):
self.master_connection = MySQLConnection(param_set_1)
self.slave_connection = MySQLConnection(param_set_2)
def __enter__(self):
self.master_connection.__enter__()
self.slave_connection.__enter__()
return self
def __exit__(self, exc_type, exc, traceback):
self.master_connection.__exit__(exc_type, exc, traceback)
self.slave_connection.__exit__(exc_type, exc, traceback)
# Some real operation functions
# Simple usage example
with DataSync() as sync:
records = sync.master_connection.fetch_records()
sync.slave_connection.push_records(records)
Run Code Online (Sandbox Code Playgroud)
问:这样调用__enter__/ __exit__手动是否可以(有什么不对)吗?
Pylint 1.1.0没有对此发出任何警告,也没有找到任何关于它的文章(在开始的谷歌链接).
那叫什么呢:
try:
# Db query
except MySQL.ServerDisconnectedException:
self.master_connection.__exit__(None, None, None)
self.master_connection.__enter__()
# Retry
Run Code Online (Sandbox Code Playgroud)
这是一个好/坏的做法?为什么?
Eri*_*ric 14
你的第一个例子不是一个好主意:
如果slave_connection.__enter__抛出异常会发生什么:
master_connection 获得其资源slave_connection 失败DataSync.__enter__ 传播异常DataSync.__exit__ 不运行master_connection 从来没有清理过!master_connection.__exit__抛出异常会发生什么?
DataSync.__exit__ 提早结束slave_connection 从来没有清理过!contextlib.ExitStack 可以帮助:
def __enter__(self):
with ExitStack() as stack:
stack.enter_context(self.master_connection)
stack.enter_context(self.slave_connection)
self._stack = stack.pop_all()
return self
def __exit__(self, exc_type, exc, traceback):
self._stack.__exit__(self, exc_type, exc, traceback)
Run Code Online (Sandbox Code Playgroud)
问同样的问题:
如果slave_connection.__enter__抛出异常会发生什么:
stack清理master_connectionmaster_connection.__exit__抛出异常会发生什么?
slave_connection在调用之前要清理干净好的,如果slave_connection.__exit__抛出异常会发生什么?
ExitStack确保调用master_connection.__exit__从属连接发生的任何事情__enter__直接调用没有错,但如果你需要在多个对象上调用它,请确保正确清理!
dan*_*ano 12
不,这没什么不对.标准库中甚至有一些地方可以执行此操作.像__enter__模块一样:
class SemLock(object):
def __init__(self, kind, value, maxvalue, *, ctx):
...
try:
sl = self._semlock = _multiprocessing.SemLock(
kind, value, maxvalue, self._make_name(),
unlink_now)
except FileExistsError:
pass
...
def __enter__(self):
return self._semlock.__enter__()
def __exit__(self, *args):
return self._semlock.__exit__(*args)
Run Code Online (Sandbox Code Playgroud)
class _TemporaryFileWrapper:
def __init__(self, file, name, delete=True):
self.file = file
self.name = name
self.delete = delete
self._closer = _TemporaryFileCloser(file, name, delete)
...
# The underlying __enter__ method returns the wrong object
# (self.file) so override it to return the wrapper
def __enter__(self):
self.file.__enter__()
return self
# Need to trap __exit__ as well to ensure the file gets
# deleted when used in a with statement
def __exit__(self, exc, value, tb):
result = self.file.__exit__(exc, value, tb)
self.close()
return result
Run Code Online (Sandbox Code Playgroud)
标准库示例不是调用multiprocessing/ tempfile用于两个对象,但如果您有一个对象负责创建/销毁多个对象的上下文而不是一个对象,则调用__enter__/ __exit__为所有对象都可以.
唯一可能的问题是正确处理__enter__ __exit__您正在管理的对象的调用的返回值.有了__enter__,您需要确保返回__exit__包装器对象的用户所需的任何内容以从__enter__调用中返回.有了state,您需要决定是否要传播上下文中发生的任何异常(通过返回with ... as <state>:),或者抑制它(通过返回__exit__).您的托管对象可以尝试以任何方式执行此操作,您需要确定对包装器对象有意义的内容.
Rol*_*ier 10
我只回答标题,即从 IPython 提示符进行__enter__调用。__exit__你可以这样做:
ctx.__enter__()
ctx.__exit__(*sys.exc_info())
Run Code Online (Sandbox Code Playgroud)