在Python中尝试直到没有错误

mur*_*d99 58 python error-handling

我在Python中有一段代码似乎在概率上导致错误,因为它正在访问服务器,有时该服务器有500内部服务器错误.我想继续尝试,直到我没有得到错误.我的解决方案是:

while True:
    try:
        #code with possible error
    except:
         continue
    else:
         #the rest of the code
         break
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎是个黑客.是否有更多的Pythonic方法来做到这一点?

小智 57

它不会变得更清洁.这不是一件非常干净的事情.充其量(无论如何,这将是更可读的,因为它的条件break是在那里while),你可以创建一个变量result = None并循环它is None.你也应该调整变量,你可以continue用语义替换也许正确pass(你不关心是否发生错误,你只是想忽略它)并删除break- 这也得到了其余的代码,只执行一次, 跳出循环.另请注意,由于文档中给出except:的原因,裸条款是邪恶.

包含以上所有内容的示例:

result = None
while result is None:
    try:
        # connect
        result = get_data(...)
    except:
         pass
# other code that uses result but is not involved in getting it
Run Code Online (Sandbox Code Playgroud)

  • 如果持续存在连接没有成功的原因,这个解决方案将在无限循环中消失. (5认同)
  • @BradKoch当然.这是问题所固有的,而且任何修复(例如整体超时或有限次数的尝试)与我描述的更改相对正交. (4认同)
  • 但任何建议的答案都应该是安全的,或者至少要注意陷阱.这不能提供100%CPU消耗的保护,并且会危及未来的读者. (3认同)

mou*_*uad 24

也许是这样的:

connected = False

while not connected:
    try:
        try_connect()
        connected = True
    except ...:
        pass
Run Code Online (Sandbox Code Playgroud)

  • 任何建议的答案都应该是安全的,或者至少要注意陷阱.这不能提供100%CPU消耗的保护,并且会危及未来的读者. (4认同)

rad*_*tek 19

这是一次尝试4次后硬盘失败,并在两次尝试之间等待2秒.根据您的需要进行更改以获得您想要的内容:

from time import sleep

for x in range(0, 4):  # try 4 times
    try:
        # msg.send()
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(2)  # wait for 2 seconds before trying to fetch the data again
    else:
        break
Run Code Online (Sandbox Code Playgroud)

这是退避的示例:

from time import sleep

sleep_time = 2
num_retries = 4
for x in range(0, num_retries):  
    try:
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(sleep_time)  # wait before trying to fetch the data again
        sleep_time *= 2  # Implement your backoff algorithm here i.e. exponential backoff
    else:
        break
Run Code Online (Sandbox Code Playgroud)

  • 我比其他人更喜欢这个答案,因为这个问题由于睡眠功能而对其他进程"很好"并且尝试次数有限. (4认同)

Bra*_*och 8

由于错误而重试时,您应该始终:

  • 实施重试限制,否则您可能会被无限循环阻塞
  • 实施延迟,否则您会过分使用资源,例如您的 CPU 或已经陷入困境的远程服务器

在解决这些问题的同时解决这个问题的一个简单的通用方法是使用退避库。一个基本的例子:

import backoff

@backoff.on_exception(
    backoff.expo,
    MyException,
    max_tries=5
)
def make_request(self, data):
    # do the request
Run Code Online (Sandbox Code Playgroud)

此代码使用实现重试逻辑的装饰器包装 make_request。每当MyException发生特定错误时,我们都会重试,重试次数限制为 5 次。 在这种情况下,指数退避是一个好主意,可以帮助最大程度地减少重试给远程服务器带来的额外负担。


pyl*_*ang 5

这些itertools.iter_except食谱封装了“重复调用函数直到引发异常”的想法。它类似于接受的答案,但配方提供了一个迭代器。

从食谱:

def iter_except(func, exception, first=None):
    """ Call a function repeatedly until an exception is raised."""
    try:
        if first is not None:
            yield first()            # For database APIs needing an initial cast to db.first()
        while True:
            yield func()
    except exception:
        pass
Run Code Online (Sandbox Code Playgroud)

你当然可以直接实现后面的代码。为方便起见,我使用了一个单独的库,more_itertools为我们实现了这个秘诀(可选)。

代码

import more_itertools as mit

list(mit.iter_except([0, 1, 2].pop, IndexError))
# [2, 1, 0]
Run Code Online (Sandbox Code Playgroud)

细节

在这里pop,为列表对象的每次迭代调用方法(或给定函数),直到IndexError引发an 。

对于您的情况,给定一些connect_function和预期的错误,您可以创建一个迭代器,重复调用该函数直到引发异常,例如

mit.iter_except(connect_function, ConnectionError)
Run Code Online (Sandbox Code Playgroud)

此时,通过循环或调用next().