也许这是一个愚蠢的(实际上并不是很实际的)问题,但我问它是因为我无法绕过它.
在研究return调用上下文管理器中的语句是否会阻止__exit__被调用时(不是没有),我发现在块之间__exit__和块之间进行类比似乎很常见(例如:https:// stackoverflow .com/a/9885287/3471881)因为:finallytry/finally
def test():
try:
return True
finally:
print("Good bye")
Run Code Online (Sandbox Code Playgroud)
将执行相同的:
class MyContextManager:
def __enter__(self):
return self
def __exit__(self, *args):
print('Good bye')
def test():
with MyContextManager():
return True
Run Code Online (Sandbox Code Playgroud)
这真的帮助我理解cm:s是如何工作的,但是在玩了一下之后我意识到如果我们返回的东西而不是打印,这个类比就不会起作用.
def test():
try:
return True
finally:
return False
test()
--> False
Run Code Online (Sandbox Code Playgroud)
虽然__exit__看似根本不会回归:
class MyContextManager:
def __enter__(self):
return self
def __exit__(self, *args):
return False
def test():
with MyContextManager():
return True
test()
--> True
Run Code Online (Sandbox Code Playgroud)
这让我想到也许你实际上不能归还任何东西__exit__,但你可以:
class MyContextManager:
def __enter__(self):
return self
def __exit__(self, *args):
return self.last_goodbye()
def last_goodbye(self):
print('Good bye')
def test():
with MyContextManager():
return True
test()
--> Good bye
--> True
Run Code Online (Sandbox Code Playgroud)
请注意,如果我们不在test()函数内部返回任何内容并不重要.
这引出了我的问题:
__exit__,如果是,为什么?是.从内部改变上下文的返回值是不可能的__exit__.
如果使用return语句退出上下文,则不能使用您的语句更改返回值context_manager.__exit__.这与try ... finally ...子句不同,因为代码finally仍然属于父函数,而在自己的范围内context_manager.__exit__运行
.
实际上,__exit__可以返回一个布尔值(True或False),Python将会理解它.它告诉Python是否应该抑制退出上下文的异常(如果有的话)(不传播到上下文之外).
请参阅此示例的返回值的含义__exit__:
>>> class MyContextManager:
... def __init__(self, suppress):
... self.suppress = suppress
...
... def __enter__(self):
... return self
...
... def __exit__(self, exc_type, exc_obj, exc_tb):
... return self.suppress
...
>>> with MyContextManager(True): # suppress exception
... raise ValueError
...
>>> with MyContextManager(False): # let exception pass through
... raise ValueError
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError
>>>
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,两个ValueErrors都会导致控件跳出上下文.在第一个块中,__exit__上下文管理器的方法返回True,因此Python会抑制此异常,并且它在REPL中没有反射.在第二个块中,上下文管理器返回False,因此Python让外部代码处理异常,由REPL打印出来.
解决方法是将结果存储在属性中而不是返回它,并稍后访问它。也就是说,如果您打算在打印之外使用该值。
例如,采用这个简单的上下文管理器:
class time_this_scope():
"""Context manager to measure how much time was spent in the target scope."""
def __init__(self, allow_print=False):
self.t0 = None
self.dt = None
self.allow_print = allow_print
def __enter__(self):
self.t0 = time.perf_counter()
def __exit__(self, type=None, value=None, traceback=None):
self.dt = (time.perf_counter() - self.t0) # Store the desired value.
if self.allow_print is True:
print(f"Scope took {self.dt*1000: 0.1f} milliseconds.")
Run Code Online (Sandbox Code Playgroud)
可以这样使用:
with time_this_scope(allow_print=True):
time.sleep(0.100)
>>> Scope took 100 milliseconds.
Run Code Online (Sandbox Code Playgroud)
或者像这样:
timer = time_this_scope()
with timer:
time.sleep(0.100)
dt = timer.dt
Run Code Online (Sandbox Code Playgroud)
与下面显示的不同,因为随着作用域结束,该对象timer将不再可访问。我们需要按照此处所述return self修改该类并向__enter__. 修改之前,会报错:
with time_this_scope() as timer:
time.sleep(0.100)
dt = timer.dt
>>> AttributeError: 'NoneType' object has no attribute 'dt'
Run Code Online (Sandbox Code Playgroud)
最后,这是一个简单的使用示例:
"""Calculate the average time spent sleeping."""
import numpy as np
import time
N = 100
dt_mean = 0
for n in range(N)
timer = time_this_scope()
with timer:
time.sleep(0.001 + np.random.rand()/1000) # 1-2 ms per loop.
dt = timer.dt
dt_mean += dt/N
print(f"Loop {n+1}/{N} took {dt}s.")
print(f"All loops took {dt_mean}s on average.)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
728 次 |
| 最近记录: |