我有一个以开头的循环for i in range(0, 100)
。正常情况下,它可以正常运行,但有时由于网络条件而失败。目前,我已对其进行了设置,以便在失败时,它将continue
在except子句中(继续到的下一个数字i
)。
我是否可以将相同的数字重新分配给i
循环并再次执行失败的循环?
我有一个以开头的循环for i in range(0, 100)
。正常情况下,它可以正常运行,但有时由于网络条件而失败。目前,我已对其进行了设置,以便在失败时,它将continue
在except子句中(继续到的下一个数字i
)。
我是否可以将相同的数字重新分配给i
循环并再次执行失败的循环?
Answers:
做一个while True
内部的for循环,把你的try
代码中,并突破从while
只有当你的代码的成功循环。
for i in range(0,100):
while True:
try:
# do stuff
except SomeSpecificException:
continue
break
continue
重试while
循环,当然不是了for
,所以(!)i
是不是 “下一个”什么-这是因为它是在同以前(失败)的腿完全一样的while
,当然。
我更喜欢限制重试的次数,这样,如果该特定项目有问题,您最终将继续进行下一个,因此:
for i in range(100):
for attempt in range(10):
try:
# do thing
except:
# perhaps reconnect, etc.
else:
break
else:
# we failed all the attempts - deal with the consequences.
else:
子句for
。
else
子句try
执行此操作,即“寻找成功,然后中断”。
except
条款,并记得用9,而不是10
在重试包是重试的代码块失败的好办法。
例如:
@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
print("Randomly wait 1 to 2 seconds between retries")
没有使用那些难看的while循环的更“实用”的方法:
def tryAgain(retries=0):
if retries > 10: return
try:
# Do stuff
except:
retries+=1
tryAgain(retries)
tryAgain()
except: tryAgain(retries+1)
最清晰的方法是显式设置i
。例如:
i = 0
while i < 100:
i += 1
try:
# do stuff
except MyException:
continue
i += 1
后面可能会的# do stuff
。
range
用于这种东西。
带有超时的通用解决方案:
import time
def onerror_retry(exception, callback, timeout=2, timedelta=.1):
end_time = time.time() + timeout
while True:
try:
yield callback()
break
except exception:
if time.time() > end_time:
raise
elif timedelta > 0:
time.sleep(timedelta)
用法:
for retry in onerror_retry(SomeSpecificException, do_stuff):
retry()
except exception:
try … except
可以使用if
语句代替a 。但是它不是pythonic。
callback
从不调用该函数。我忘记了括号,取而代之callback()
。
for _ in range(5):
try:
# replace this with something that may fail
raise ValueError("foo")
# replace Exception with a more specific exception
except Exception as e:
err = e
continue
# no exception, continue remainder of code
else:
break
# did not break the for loop, therefore all attempts
# raised an exception
else:
raise err
我的版本与上述几种类似,但是没有使用单独的while
循环,并且如果所有重试均失败,则会重新引发最新的异常。可以err = None
在顶部显式设置,但不是严格必需的,因为只有在else
出现错误并因此err
被设置时才执行最后一个块。
retrying
:tenacity
和backoff
(2020更新)的替代方案将重试库是以前的路要走,但可悲的是它有一些缺陷,自2016年其他选择似乎还没有得到任何更新补偿和坚韧。在撰写本文时,坚韧程度更高的GItHub星星(2.3k和1.2k)并已更新,因此我选择使用它。这是一个例子:
from functools import partial
import random # producing random errors for this example
from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type
# Custom error type for this example
class CommunicationError(Exception):
pass
# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
retry,
stop=stop_after_delay(10), # max. 10 seconds wait.
wait=wait_fixed(0.4), # wait 400ms
retry=retry_if_exception_type(CommunicationError),
)()
@retry_on_communication_error
def do_something_unreliable(i):
if random.randint(1, 5) == 3:
print('Run#', i, 'Error occured. Retrying.')
raise CommunicationError()
上面的代码输出类似:
Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.
tenacity.retry
在韧度GitHub页面上列出了的更多设置。
如果您想要一个没有嵌套循环且无需break
成功调用的解决方案,则可retriable
以为任何可迭代的对象开发一个快速包装。这是我经常遇到的网络问题的示例-保存的身份验证过期。它的使用将如下所示:
client = get_client()
smart_loop = retriable(list_of_values):
for value in smart_loop:
try:
client.do_something_with(value)
except ClientAuthExpired:
client = get_client()
smart_loop.retry()
continue
except NetworkTimeout:
smart_loop.retry()
continue
这是我对这个问题的看法。以下retry
功能支持以下功能:
import time
def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
attempt = 1
while True:
try:
return func()
except Exception as ex:
if not isinstance(ex, ex_type):
raise ex
if 0 < limit <= attempt:
if logger:
logger.warning("no more attempts")
raise ex
if logger:
logger.error("failed execution attempt #%d", attempt, exc_info=ex)
attempt += 1
if logger:
logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
time.sleep(wait_ms / 1000)
wait_ms *= wait_increase_ratio
用法:
def fail_randomly():
y = random.randint(0, 10)
if y < 10:
y = 0
return x / y
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))
logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)
有关更多信息,请参见我的帖子。
这是我的解决方法:
j = 19
def calc(y):
global j
try:
j = j + 8 - y
x = int(y/j) # this will eventually raise DIV/0 when j=0
print("i = ", str(y), " j = ", str(j), " x = ", str(x))
except:
j = j + 1 # when the exception happens, increment "j" and retry
calc(y)
for i in range(50):
calc(i)
我最近与我的python合作解决了这个问题,很高兴与stackoverflow访问者分享它,如果需要的话请提供反馈。
print("\nmonthly salary per day and year converter".title())
print('==' * 25)
def income_counter(day, salary, month):
global result2, result, is_ready, result3
result = salary / month
result2 = result * day
result3 = salary * 12
is_ready = True
return result, result2, result3, is_ready
i = 0
for i in range(5):
try:
month = int(input("\ntotal days of the current month: "))
salary = int(input("total salary per month: "))
day = int(input("Total Days to calculate> "))
income_counter(day=day, salary=salary, month=month)
if is_ready:
print(f'Your Salary per one day is: {round(result)}')
print(f'your income in {day} days will be: {round(result2)}')
print(f'your total income in one year will be: {round(result3)}')
break
else:
continue
except ZeroDivisionError:
is_ready = False
i += 1
print("a month does'nt have 0 days, please try again")
print(f'total chances left: {5 - i}')
except ValueError:
is_ready = False
i += 1
print("Invalid value, please type a number")
print(f'total chances left: {5 - i}')
仅在try子句成功时增加循环变量
range(100)
不使用第一个参数而使用。如果您使用Python 2.x,甚至可以使用xrange(100)
,这将生成一个迭代器并使用更少的内存。(这并不意味着只包含100个对象。)