组合Python上下文管理器:一个难题
我对如何将Python上下文管理器可以做的所有事情安排到适当的位置感到困惑 据我所知,可能用于构建上下文管理器的元素包括:组合Python上下文管理器:一个难题,python,try-catch,with-statement,contextmanager,Python,Try Catch,With Statement,Contextmanager,我对如何将Python上下文管理器可以做的所有事情安排到适当的位置感到困惑 据我所知,可能用于构建上下文管理器的元素包括: 经常发生的事情 B:C需要一些准备工作 C:创建并建立上下文中使用的对象X D:在上下文开始之前,使用成功建立的X做一些事情 E:将X返回到上下文(供作为使用) F:当上下文结束时一切正常时,用X结束 G:在进入上下文之前,处理C和B中失败的后果 H:根据上下文处理失败的后果 我想我大致了解了这些元素在上下文管理器函数中的位置,但完全不知道如何在类中排列它们 是否有一个
- 经常发生的事情
- B:C需要一些准备工作
- C:创建并建立上下文中使用的对象X
- D:在上下文开始之前,使用成功建立的X做一些事情
- E:将X返回到上下文(供
作为
使用)
- F:当上下文结束时一切正常时,用X结束
- G:在进入上下文之前,处理C和B中失败的后果
- H:根据上下文处理失败的后果
我想我基本上理解了通过函数实现时上下文管理器的行为:
from contextlib import contextmanager
@contextmanager
def log_file_open(oec_data, build_description, log_dir):
# A: Something that always happens
try:
# B: Some stuff needed to make a_thing
a_thing = establish_thing_in_a_way_that_might_fail() # C
# D: Some things that happen using a_thing at context start
yield a_thing # E
# F: Wrap up with a_thing when all is well
except:
# G: Deal the consequences of failure in try or...
# H: Deal the consequences of failure in context
finally:
# Could F go here instead?
例如,要打开一个文件,在成功打开和关闭时,应该将某些内容写入该文件,但如果出现问题,则应该清除该文件,我可以编写
from contextlib import contextmanager
@contextmanager
def log_file_open(oec_data, build_description, log_dir):
print('Entering context...')
try:
usable_file_name = get_some_name()
a_thing = open(usable_file_name, mode='w')
a_thing.write('Logging context started.')
yield a_thing
a_thing.write('Logging context ended.')
except:
a_thing.close()
os.remove(a_thing.name)
raise
但我不确定这是否正确,我很困惑它如何映射到类中的\uuuuu enter()\uuuuu
和\uuuu exit()\uuuuuu
的使用。是(示意图):
您在生成上下文值时混淆了错误处理,而在上下文本身中混淆了错误处理。最好写下:
@contextmanager
def fn(...):
value = ... # A, B, C, D: setup
try:
yield value # E: pass value to client
except: # or better, finally:
... # F, H: cleanup
这样,您就知道您只处理源于客户端代码的异常,并且当您知道安装成功时,您就简化了清理代码。在设置代码中处理异常通常没有意义;您不希望客户端代码必须处理None
上下文值。这意味着\uuuu enter\uuuu
只是:
def __enter__(self):
self.value = ... # A, B, C, D: setup
return self.value # E: pass value to client
def __exit__(self, type, value, traceback):
... # F, H: cleanup
return False # don't suppress any exception
如果\uuuuuuuuuuuuuuuuuuuuu
引发异常,则不会调用\uuuuuuuuuuuu
还要注意的是,finally
比好,除了
,除非您计划从客户机代码中抑制异常,这很少有用。所以\uuuu退出\uuuu
就是:
def __enter__(self):
self.value = ... # A, B, C, D: setup
return self.value # E: pass value to client
def __exit__(self, type, value, traceback):
... # F, H: cleanup
return False # don't suppress any exception
我认为你的理解基本上是正确的。上下文管理器是一个对象,它通过其
\uuuuuuuuuuuuuuuuuu
和\uuuuuuuuuu
方法管理上下文。因此,\uuuuu init\uuuu
中发生的事情在对象的生命周期内保持不变。
让我们看一个具体的例子:
class CMan(object):
def __init__(self, *parameters):
"Creates a new context manager"
print "Creating object..."
def __enter__(self):
"Enters the manager (opening the file)"
print "Entering context..."
a_thing = self # Or any other relevant value to be used in this context
print "Returning %s" % a_thing
return a_thing
def __exit__(self, type, value, traceback):
"Exits the context"
if type is None:
print "Exiting with no exception -> Wrapping up"
return
print "Exiting with exception %s" % type
这将被用作:
>>> with CMan(1,2,3) as x:
... print 1 + 1
Creating object...
Entering context...
Returning <__main__.CMan object at 0x02514F70>
2
Exiting with no exception -> Wrapping up
注意,在被0除错误的情况下,当\uuu exit\uu
返回True
时,错误不会传播。在其他情况下,它在退出上下文管理器后引发。您可以考虑调用上下文管理器:
>>> with X as x:
... f(x)
等同于:
>>> x = X.__enter__()
>>> try:
... exc = None
... f(x)
... except Exception as e:
... exc = e
... finally:
... handled = X.__exit__(exc)
... if exc and not handled:
... raise exc
当然,如果在方法\uuuuu enter\uuuu
或\uuuu exit\uuuuu
中引发异常,则应适当处理该异常,例如,如果生成一个东西
可能失败。通过查找“Python with statement”,您可以在web上找到大量资源,这通常是您引用此模式的方式(尽管上下文管理器确实更正确)
>>> x = X.__enter__()
>>> try:
... exc = None
... f(x)
... except Exception as e:
... exc = e
... finally:
... handled = X.__exit__(exc)
... if exc and not handled:
... raise exc