Python 上下文管理器如何尝试执行代码?
How can a Python context manager try to execute code?
我正在尝试编写一个小型上下文管理器,它会尝试重复执行一些代码,直到代码正常工作或直到进行了指定次数的尝试。我试图写这篇文章,但在让上下文管理器处理屈服时遇到问题时遇到了困难:
Exception RuntimeError: 'generator ignored GeneratorExit'
我应该如何编码?
import contextlib
import random
def main():
with nolube():
print(1 / random.randint(0, 1))
@contextlib.contextmanager
def nolube(
tries = None # None: try indefinitely
):
"""
Create a context for trying something repeatedly.
"""
tries_done = 0
rekt = True
if tries is None:
while rekt is True:
try:
yield
rekt = False
except:
tries_done += 1
pass
else:
while rekt is True and tries_done <= tries:
try:
yield
rekt = False
except:
tries_done += 1
pass
if __name__ == "__main__":
main()
简短的回答是:在 Python.
中,您不能使用上下文管理器真正做到这一点
我们只能在上下文管理器中 yield
一次,因此在 while
循环中使用 yield
没有意义。这与 Ruby
block
中使用的 yield
不同。
而且我们也无权访问代码主体,例如,我们不会自动获得可以重用的 function
之类的东西。
所以,不,如果您确实想要实现可重用的 retry
逻辑,请改用函数。
def retry(func, n_times=None):
i = 1
while True:
try:
return func()
except Exception:
i += 1
if n_times and i >= n_times:
raise
@contextlib.contextmanager
有很明确的约定;它只会恢复 一次。它不能用于重新运行代码。
事实上,您根本无法使用上下文管理器来控制重复。您在这里需要一个循环,而不是上下文管理器。上下文管理器不控制块,仅在进入和退出时通知它。
使用 tenacity
package* instead; it provides a decorator. The decorator wraps a function in a while True
loop 将为您重新运行 功能。
您可以通过将 print()
语句移动到一个用 @retry
修饰的函数中,然后调用该函数来将其应用于您的案例:
import random
from tenacity import retry
@retry
def foo():
print(1 / random.randint(0, 1))
def main():
foo()
* 这个答案最初推荐 retrying
package but this was forked into a new package with updated API 该项目处于休眠状态时。
你不能那样做。 Python 中的上下文管理器只是一个协议,它:
- 来电
__enter__
- 执行一个或多个语句
- 来电
__exit__
第 3 点保证会发生,这使得它非常适合处理资源释放等。但这里的重点是第 2 点:上下文管理器将 运行 代码 在 上下文中,然后处理 3。到那时,包装的代码已经消失,被遗忘并且永远无法访问,所以你不能 'call it again'。 contextlib
提供了一个很好的 API 来定义你的上下文管理器,只需将它作为一个函数来实现:
@contextmanager
def ctxt():
# 1: __enter__ code
yield
# 3: __exit__ code
并且文档清楚specifies:
The function being decorated must return a generator-iterator when called. This iterator must yield exactly one value, which will be bound to the targets in the with statement’s as clause, if any.
所以前一点仍然存在。
你可以做的就是把它放在一个函数中,然后用你的[=44]装饰那个函数=]逻辑:
def dec(f):
def decorated(*args, **kwargs):
while True:
try:
return f(*args, **kwargs)
except:
pass
return decorated
但这与上下文管理器完全无关。
我正在尝试编写一个小型上下文管理器,它会尝试重复执行一些代码,直到代码正常工作或直到进行了指定次数的尝试。我试图写这篇文章,但在让上下文管理器处理屈服时遇到问题时遇到了困难:
Exception RuntimeError: 'generator ignored GeneratorExit'
我应该如何编码?
import contextlib
import random
def main():
with nolube():
print(1 / random.randint(0, 1))
@contextlib.contextmanager
def nolube(
tries = None # None: try indefinitely
):
"""
Create a context for trying something repeatedly.
"""
tries_done = 0
rekt = True
if tries is None:
while rekt is True:
try:
yield
rekt = False
except:
tries_done += 1
pass
else:
while rekt is True and tries_done <= tries:
try:
yield
rekt = False
except:
tries_done += 1
pass
if __name__ == "__main__":
main()
简短的回答是:在 Python.
中,您不能使用上下文管理器真正做到这一点我们只能在上下文管理器中 yield
一次,因此在 while
循环中使用 yield
没有意义。这与 Ruby
block
中使用的 yield
不同。
而且我们也无权访问代码主体,例如,我们不会自动获得可以重用的 function
之类的东西。
所以,不,如果您确实想要实现可重用的 retry
逻辑,请改用函数。
def retry(func, n_times=None):
i = 1
while True:
try:
return func()
except Exception:
i += 1
if n_times and i >= n_times:
raise
@contextlib.contextmanager
有很明确的约定;它只会恢复 一次。它不能用于重新运行代码。
事实上,您根本无法使用上下文管理器来控制重复。您在这里需要一个循环,而不是上下文管理器。上下文管理器不控制块,仅在进入和退出时通知它。
使用 tenacity
package* instead; it provides a decorator. The decorator wraps a function in a while True
loop 将为您重新运行 功能。
您可以通过将 print()
语句移动到一个用 @retry
修饰的函数中,然后调用该函数来将其应用于您的案例:
import random
from tenacity import retry
@retry
def foo():
print(1 / random.randint(0, 1))
def main():
foo()
* 这个答案最初推荐 retrying
package but this was forked into a new package with updated API 该项目处于休眠状态时。
你不能那样做。 Python 中的上下文管理器只是一个协议,它:
- 来电
__enter__
- 执行一个或多个语句
- 来电
__exit__
第 3 点保证会发生,这使得它非常适合处理资源释放等。但这里的重点是第 2 点:上下文管理器将 运行 代码 在 上下文中,然后处理 3。到那时,包装的代码已经消失,被遗忘并且永远无法访问,所以你不能 'call it again'。 contextlib
提供了一个很好的 API 来定义你的上下文管理器,只需将它作为一个函数来实现:
@contextmanager
def ctxt():
# 1: __enter__ code
yield
# 3: __exit__ code
并且文档清楚specifies:
The function being decorated must return a generator-iterator when called. This iterator must yield exactly one value, which will be bound to the targets in the with statement’s as clause, if any.
所以前一点仍然存在。
你可以做的就是把它放在一个函数中,然后用你的[=44]装饰那个函数=]逻辑:
def dec(f):
def decorated(*args, **kwargs):
while True:
try:
return f(*args, **kwargs)
except:
pass
return decorated
但这与上下文管理器完全无关。