在 Python 中尝试直到没有错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4606919/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 16:30:44  来源:igfitidea点击:

In Python try until no error

pythonerror-handling

提问by murgatroid99

I have a piece of code in Python that seems to cause an error probabilistically because it is accessing a server and sometimes that server has a 500 internal server error. I want to keep trying until I do not get the error. My solution was:

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

while True:
    try:
        #code with possible error
    except:
         continue
    else:
         #the rest of the code
         break

This seems like a hack to me. Is there a more Pythonic way to do this?

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

采纳答案by mouad

It won't get much cleaner. This is not a very clean thing to do. At best (which would be more readable anyway, since the condition for the breakis up there with the while), you could create a variable result = Noneand loop while it is None. You should also adjust the variables and you can replace continuewith the semantically perhaps correct pass(you don't care if an error occurs, you just want to ignore it) and drop the break- this also gets the rest of the code, which only executes once, out of the loop. Also note that bare except:clauses are evil for reasons given in the documentation.

它不会变得更干净。这不是一件很干净的事情。充其量(无论如何,这将更具可读性,因为 the 的条件break与 the 一起存在while),您可以创建一个变量result = None并在 it 时循环is None。你还应该调整变量,你可以continue用语义上可能正确的替换pass(你不在乎是否发生错误,你只想忽略它)并删除break- 这也得到了其余的代码,它只执行一次, 跳出循环。还要注意,由于文档中给出except:的原因,裸子句是邪恶

Example incorporating all of the above:

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

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

回答by mouad

Maybe something like this:

也许是这样的:

connected = False

while not connected:
    try:
        try_connect()
        connected = True
    except ...:
        pass

回答by virhilo

Maybe decorator based? You can pass as decorator arguments list of exceptions on which we want to retry and/or number of tries.

也许基于装饰器?您可以将我们想要重试的异常列表和/或尝试次数作为装饰器参数传递。

def retry(exceptions=None, tries=None):
    if exceptions:
        exceptions = tuple(exceptions)
    def wrapper(fun):
        def retry_calls(*args, **kwargs):
            if tries:
                for _ in xrange(tries):
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
            else:
                while True:
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
        return retry_calls
    return wrapper


from random import randint

@retry([NameError, ValueError])
def foo():
    if randint(0, 1):
        raise NameError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def bar():
    if randint(0, 1):
        raise ValueError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def baz():
    while True:
        raise ValueError('FAIL!')

foo()
bar()
baz()

of course the 'try' part should be moved to another funcion becouse we using it in both loops but it's just example;)

当然,“尝试”部分应该移到另一个函数,因为我们在两个循环中都使用它,但这只是示例;)

回答by radtek

Here is a short piece of code I use to capture the error as a string. Will retry till it succeeds. This catches all exceptions but you can change this as you wish.

这是我用来将错误捕获为字符串的一小段代码。会重试直到成功。这会捕获所有异常,但您可以根据需要进行更改。

start = 0
str_error = "Not executed yet."
while str_error:
    try:
        # replace line below with your logic , i.e. time out, max attempts
        start = raw_input("enter a number, 0 for fail, last was {0}: ".format(start))
        new_val = 5/int(start)
        str_error=None
    except Exception as str_error:
         pass

WARNING:This code will be stuck in a forever loop until no exception occurs. This is just a simple example and MIGHT require you to break out of the loop sooner or sleep between retries.

警告:此代码将陷入永远循环,直到没有异常发生。这只是一个简单的例子,可能需要您尽快跳出循环或在重试之间休眠。

回答by radtek

Here is one that hard fails after 4 attempts, and waits 2 seconds between attempts. Change as you wish to get what you want form this one:

这是 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

Here is an example with backoff:

这是一个带有退避功能的示例:

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

回答by Harry Harrison

Here's an utility function that I wrote to wrap the retry until success into a neater package. It uses the same basic structure, but prevents repetition. It could be modified to catch and rethrow the exception on the final try relatively easily.

这是我编写的一个实用函数,用于将重试直到成功包装成一个更整洁的包。它使用相同的基本结构,但防止重复。可以修改它以相对容易地在最后一次尝试中捕获并重新抛出异常。

def try_until(func, max_tries, sleep_time):
    for _ in range(0,max_tries):
        try:
            return func()
        except:
            sleep(sleep_time)
    raise WellNamedException()
    #could be 'return sensibleDefaultValue'

Can then be called like this

然后可以这样调用

result = try_until(my_function, 100, 1000)

If you need to pass arguments to my_function, you can either do this by having try_untilforward the arguments, or by wrapping it in a no argument lambda:

如果您需要将参数传递给my_function,您可以通过try_until转发参数或将其包装在无参数 lambda 中来做到这一点:

result = try_until(lambda : my_function(x,y,z), 100, 1000)

回答by pylang

The itertools.iter_exceptrecipes encapsulates this idea of "calling a function repeatedly until an exception is raised". It is similar to the accepted answer, but the recipe gives an iterator instead.

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

From the recipes:

从食谱:

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

You can certainly implement the latter code directly. For convenience, I use a separate library, more_itertools, that implements this recipe for us (optional).

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

Code

代码

import more_itertools as mit

list(mit.iter_except([0, 1, 2].pop, IndexError))
# [2, 1, 0]


Details

细节

Here the popmethod (or given function) is called for every iteration of the list object until an IndexErroris raised.

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

For your case, given some connect_functionand expected error, you can make an iterator that calls the function repeatedly until an exception is raised, e.g.

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

mit.iter_except(connect_function, ConnectionError)

At this point, treat it as any other iterator by looping over it or calling next().

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

回答by noname

Like most of the others, I'd recommend trying a finite number of times and sleeping between attempts. This way, you don't find yourself in an infinite loop in case something were to actually happen to the remote server.

像大多数其他人一样,我建议尝试有限的次数并在尝试之间睡觉。这样,您就不会发现自己处于无限循环中,以防远程服务器实际发生某些事情。

I'd also recommend continuing only when you get the specific exception you're expecting. This way, you can still handle exceptions you might not expect.

我还建议仅在您获得预期的特定异常时继续。这样,您仍然可以处理您可能不期望的异常。

from urllib.error import HTTPError
import traceback
from time import sleep


attempts = 10
while attempts > 0:
    try:
        #code with possible error
    except HTTPError:
        attempts -= 1
        sleep(1)
        continue
    except:
        print(traceback.format_exc())

    #the rest of the code
    break

Also, you don't need an else block. Because of the continue in the except block, you skip the rest of the loop until the try block works, the while condition gets satisfied, or an exception other than HTTPError comes up.

此外,您不需要 else 块。由于 except 块中的 continue,您可以跳过循环的其余部分,直到 try 块起作用、满足 while 条件或出现 HTTPError 以外的异常。

回答by user9311010

e = ''
while e == '':
    try:
        response = ur.urlopen('https://https://raw.githubusercontent.com/MrMe42/Joe-Bot-Home-Assistant/mac/Joe.py')
        e = ' '
    except:
        print('Connection refused. Retrying...')
        time.sleep(1)

This should work. It sets e to '' and the while loop checks to see if it is still ''. If there is an error caught be the try statement, it prints that the connection was refused, waits 1 second and then starts over. It will keep going until there is no error in try, which then sets e to ' ', which kills the while loop.

这应该有效。它将 e 设置为 '',while 循环检查它是否仍然是 ''。如果 try 语句中捕获到错误,它会打印连接被拒绝,等待 1 秒然后重新开始。它会一直运行,直到 try 中没有错误,然后将 e 设置为 ' ',从而终止 while 循环。

回答by Brad Koch

When retrying due to error, you should always:

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

  • implement a retry limit, or you may get blocked on an infinite loop
  • implement a delay, or you'll hammer resources too hard, such as your CPU or the already distressed remote server
  • 实施重试限制,否则您可能会被无限循环阻塞
  • 实施延迟,否则您会过分使用资源,例如您的 CPU 或已经陷入困境的远程服务器

A simple generic way to solve this problem while covering those concerns would be to use the backofflibrary. A basic example:

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

import backoff

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

This code wraps make_request with a decorator which implements the retry logic. We retry whenever our specific error MyExceptionoccurs, with a limit of 5 retries. Exponential backoffis a good idea in this context to help minimize the additional burden our retries place on the remote server.

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