在Python中尝试直到没有错误

时间:2011-01-05 17:08:34

标签: python error-handling

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

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

这对我来说似乎是个黑客。有更多的Pythonic方法吗?

13 个答案:

答案 0 :(得分:48)

它不会变得更清洁。这不是一件非常干净的事情。充其量(无论如何都会更具可读性,因为break的条件与while一致),您可以创建变量result = None并循环{{1} }。您还应该调整变量,并且可以用语义上正确的is None替换continue(您不关心是否发生错误,您只是想忽略它)并删除pass - 这也得到了剩下的代码,它只执行一次,不在循环中。另请注意,由于[{3}} {。}}原因,break个条款是不正当的。

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

except:

答案 1 :(得分:20)

也许是这样的:

connected = False

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

答案 2 :(得分:18)

这是一次尝试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

以下是退避的示例:

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

答案 3 :(得分:3)

itertools.iter_except配方封装了这个"重复调用函数的想法,直到引发异常为止#34;。它类似于接受的答案,但是配方给出了迭代器。

从食谱中:

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

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

<强>代码

import more_itertools as mit

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

<强>详情

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

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

mit.iter_except(connect_function, ConnectionError)

此时,通过循环或调用next()将其视为任何其他迭代器。

答案 4 :(得分:2)

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

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'

然后可以这样调用

result = try_until(my_function, 100, 1000)

如果你需要将参数传递给my_function,你可以通过让try_until转发参数,或者将它包装在无参数lambda中来实现:

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

答案 5 :(得分:1)

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

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()

当然'try'部分应该转移到另一个函数因为我们在两个循环中使用它但它只是示例;)

答案 6 :(得分:1)

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

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

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

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

答案 7 :(得分:1)

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)

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

答案 8 :(得分:0)

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

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

警告:此代码将停留在永久循环中,直到不会发生异常。这只是一个简单的例子,MIGHT要求您尽快摆脱循环或在重试之间休眠。

答案 9 :(得分:0)

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

  • 实施重试限制,否则您可能会陷入无限循环
  • 实施延迟,否则您将过于费力地浪费资源,例如您的CPU或已经苦恼的远程服务器

解决这些问题的一种简单通用方法是使用backoff库。一个基本的例子:

import backoff

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

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

答案 10 :(得分:0)

retrying library on pypi呢? 我已经使用了一段时间了,它确实可以满足我的要求(更多)(重试错误,重试“无”,超时重试)。下面是他们网站上的示例:

putchar(c);

答案 11 :(得分:0)

我现在正在尝试这样做,这就是我想出的;

theory T : ?Meta =
  structure s1 : ?S = ❚
  structure s2 : ?S = ❚

  n: ℕ ❙

  c_usage1 = n s1/+ n ❙
  c_usage2 = n s2/+ n ❙
  c_usage_combined = (n s1/+ n) ∨ (n s2/+ n) ❙

  d_usage1 = n s1/|- n ∶ n ❙
  d_usage2 = n s2/|- n ∶ n ❙
  d_usage_combined = (n s1/|- n ∶ n) ∨ (n s2/|- n ∶ n) ❙
❚

答案 12 :(得分:0)

这里有许多不同的答案,但我感到没有一个可以成功满足我的需求(或更重要的是,提问者的需求),因此我结合了一些出色的答案,并放入了自己的不太秘密的调味料提出以下配方:

successful = False
attempts = [0]
authorized_attempts = 10
while sum(attempts) < authorized_attempts and not successful:
    try:
        attempts.append(1)
        if EXPECTED_RESULT in result():
            print(f"Success on attempt number {sum(attempts)}.")
            attempts = [0]
            successful = True
    except Exception:
         print(f"Failure on attempt number {sum(attempts)}.")
         time.sleep(5)