【问题标题】:How to catch multiple exception of same type in decorator如何在装饰器中捕获多个相同类型的异常
【发布时间】:2016-05-12 10:06:10
【问题描述】:

我想知道是否有办法在另一个装饰器中捕获一个装饰器引起的所有异常并处理这些异常。

但是,在为我的问题想出某种复杂的解决方案之前,我想我会问专家,看看我是否遗漏了什么。

我的应用程序如下所示:

输入将作为numGen 函数给出。这个numGen 函数将输入添加到随机数。在我的示例中,我正在检查 10 个不同的随机数。

请注意,输入以及随机数生成的范围也会有所不同。

一旦您在 Python 中点击引发用户定义的异常,似乎无法恢复执行测试。但在我的场景中,我想检查不同输入值发生的错误。但是当前的实现限制了继续检查不同输入向量的错误。

但是,在为我的问题想出某种复杂的解决方案之前,我想我会问专家,看看我是否遗漏了什么。不幸的是,如果不进行重大重构,我无法轻易改变这种范式。

我的应用程序如下所示: •输入将作为numGen 函数给出。此 numGen 函数将输入添加到随机数。在我的示例中,我正在检查 10 个不同的随机数。 •注意输入也将随着随机数生成范围的变化而变化

**:编辑异常类

from random import randint
class RND_ERROR(Exception):
    def __init__(self, ErrNum, *arg, **kwargs):
        self.RND_ERR_NR = int(ErrNum)
        self.RND_ERR_MSG = ""         
        if self.RND_ERR_NR == 0x00000000:
            self.RND_ERR_MSG = "NO ERROR"
        elif self.RND_ERR_NR == 0x00000001:
            self.RND_ERR_MSG = "RANDOM NUMBER IS ONE"
        elif self.RND_ERR_NR == 0x00000002:
            self.RND_ERR_MSG = "RANDOM NUMBER IS TWO"
        elif self.RND_ERR_NR == 0x00000003:
            self.RND_ERR_MSG = "RANDOM NUMBER IS THREE"
        else:
            self.RND_ERR_MSG = "RANDOM NUMBER GREATER THAN THREE"
    def __str__(self):
        return "RND ERROR (0x%08X): %s" % (self.RND_ERR_NR,self.RND_ERR_MSG)

    def __repr__(self):
       return self.__str__()



def handleError(func):
    errors =[]
    def wrapper(arg1):
        try:
            result = func(arg1)
        except MyException as e:
            print errors.append(e)
    return wrapper

def findError(func):
    def wrapper(arg1):
        result = func(arg1)
        print result
        for k, v in result.iteritems():
            error_nr = v % 2
            if error_nr ==0:
                pass
            elif error_nr > 0:
                raise RND_ERROR(errornum) #edited lines
    return wrapper

@handleError
@findError
def numGen(input):
    from random import randint
    result= {}
    errors = []
    for i in range(9):
        j = (randint(0,4))
        result[i] = input + j
        result.update()    # This line does nothing...
    return result
if __name__ == '__main__':
    numGen(4)

函数numGen

此函数接受输入并生成 10 个随机数。 10 个随机数与索引一起保存在结果字典中。根据结果​​字典调用查找错误装饰器。

装饰者 1findError:

在特定条件下,它会针对不同的结果生成异常,如果发生错误,则会引发异常。

装饰者 2handleError:

捕获之前装饰器中引起的所有异常,追加到列表中。稍后再提出。

问题陈述:

  1. 我无法在 try 块中遍历字典,因此无法捕获特定错误。

  2. 我想检查每个输入是否发生错误,如果发生任何错误引发异常并存储哪个输入的信息 例如: - 针对不同的输入引发异常。捕获异常,并存储发生异常的输入值以及异常类型(例如)。

  3. 如何通过创建聚合异常(如 MyExceptionsCollection)来聚合异常,该异常将保存并引发数组而不是打印。

  4. 如何重新引发确切的异常或所有发生的异常

注意:实现必须使用 2 个装饰器,一个用于生成错误和捕获错误。因为我将有 200-250 个函数,所以我需要检查所有这些函数的错误

请帮助我如何实现这一目标。除了例外,任何更好的解决方案也是受欢迎的。

提前致谢

【问题讨论】:

  • 您似乎正在尝试将异常机制用作消息管道。为什么不改用multiprocessing.Pipe 类?
  • @max 您能否将异常机制的示例代码 sn-p 添加为消息管道?
  • 我考虑了一下,并决定这里最好的解决方案是使用如下生成器 (pythonfiddle.com/exception-generator)。因为抛出会导致退出调用栈,所以你需要实现从某个点重新进入那里。
  • 对应链接失效。要求你放示例代码sn-ps
  • 对不起。将代码放在答案中。

标签: python exception dictionary python-decorators


【解决方案1】:

你不需要做任何复杂的异常。认为你这样做是造成问题的原因。您真正需要的是(我的猜测,因为正如其他人所说,这不是很清楚)是处理 numGen 的返回值,并根据它们做一些事情(也许,引发异常)。

一旦你用这种方式表达你的目标,很明显你需要一些更简单的东西。只是一个装饰器,它存储 numGen 返回的所有“错误”值(根据您对错误的定义)并允许您稍后处理它们。所以这是我的解决方案:

# Using a class as a decorator, so you can easily access its .wrong_values attribute
class ErrorHandler:
     def __init__(self, func):
         self.func = func
         self.wrong_values = []

     def __call__(self, arg):
         # Remove the first line if you want to keep wrong values from previous calls
         self.wrong_values.clear()
         result = self.func(arg)
         self.register_errors(result)
         return result

     def register_errors(self, result):
         wrong_values = [(k, v) for k, v in result.iteritems() if v % 2]
         self.wrong_values += wrong_values

@ErrorHandler
def numGen(input):
    from random import randint
    # You say you want 10 numbers, so the argument to range should be 10
    # And you can use dict-comp, which requires only one line and is faster
    return {i: randint(0, 4) + input for i in range(10)}

if __name__ == '__main__':
    print 'result', numGen(4)
    print 'wrong values', numGen.wrong_values

当我运行它时,我得到了

result {0: 4, 1: 8, 2: 6, 3: 8, 4: 7, 5: 5, 6: 6, 7: 5, 8: 6, 9: 5}
wrong values [(4, 7), (5, 5), (7, 5), (9, 5)]

现在您可以轻松访问numGen.wrong_values;对他们做任何你想做的事。

回顾你的问题陈述:

问题陈述:

我无法在 try 块中迭代字典,所以我是 无法捕获特定错误。

没问题,因为您不需要 try 块。

如何通过创建聚合异常来聚合异常,例如 MyExceptionsCollection,它将保存数组并引发它 印刷。

我不知道聚合异常是什么。我认为收集值并从收集的值中引发异常(或其他)更为明智。这是实现的。

如何重新引发确切的异常或所有发生的异常

同样,这是一个人为的问题,因为您认为必须首先引发异常才能“聚合”它们。你没有。相反,您可以聚合值并处理它们。

注意:实现必须使用 2 个装饰器,一个用于生成错误 并捕捉错误。因为我将有 200-250 个函数,所以我需要 检查所有这些功能的错误

我认为不需要两个装饰器。如果您对不同的功能有不同的错误捕获逻辑,只需子类化ErrorHandler 并覆盖register_errors 方法。 (使用类而不是函数作为装饰器的另一个优势。)

【讨论】:

  • 太棒了!!我还需要将 {i: randint(0, 4) + input for i in range(10)} 'i' 值注册为错误。有可能吗?
  • @Sana 很简单。只需更改register_errors 的第一行。请参阅编辑后的答案。
  • 请看问题。请询问,如果问题陈述不清楚。对问题中的延迟更新表示歉意。
【解决方案2】:

@Sana 我回答了您最近发布的Handing multiple exception of same type and resume execution in python

这个问题似乎是对您原来问题的重述,所以原来的答案仍然适用。 :-)

这里的代码 sn-p 可以在你的装饰器中使用。

记住 - Python 不能引发多个错误 - 第一个未捕获的错误将终止执行。如果您需要知道哪些项目失败了,您可以收集错误,然后将它们格式化为“主”错误。

【讨论】:

  • 请看问题。请询问,如果问题陈述不清楚。对问题中的延迟更新表示歉意。
【解决方案3】:

很难理解您的要求。这是我最好的猜测。

findErrors 检查并收集错误。如果有,它会引发异常。否则它只返回结果。

handleErrors 打印异常(如果有)。否则,它只返回结果。

因为您使用了 print 语句,所以我认为您使用的是 Python 2.7。

class MyException(Exception):pass

def handleError(func):
    def wrapper(arg1):
        try:
            return func(arg1)

        except MyException as e:
            print "Error:", e.args

    return wrapper

def findError(func):
    def wrapper(arg1):
        errors = []
        result = func(arg1)

        for k, v in result.iteritems():
            error_nr = v % 2
            if error_nr > 0:
                errors.append((k, v, error_nr))

        if errors:
            raise MyException, errors

        return result

    return wrapper

@handleError
@findError
def numGen(input):
    from random import randint
    result= {}
    for i in range(9):
        j = randint(0,4)
        result[i] = input + j
    return result

if __name__ == '__main__':
    numGen(4)

【讨论】:

    【解决方案4】:

    我建议使用生成器而不是抛出异常:

    class MyException(Exception):pass
    
    
    def handleError(func):
        errors =[]
        def wrapper(arg1):
            result = func(arg1)
    
            for err in findError(result):
                errors.append(err)
    
            print errors
            return result
    
        return wrapper
    
    def findError(result):
        print result
        for k, v in result.iteritems():
            error_nr = v % 2
            if error_nr ==0:
                pass
            elif error_nr > 0:
                yield MyException
    
    @handleError
    def numGen(input):
        from random import randint
        result= {}
        errors = []
        for i in range(9):
            j = (randint(0,4))
            result[i] = input + j
        return result
    
    if __name__ == '__main__':
        numGen(4)
    

    【讨论】:

    • 能否请您告诉我,我该如何重新提出此异常?
    • 您要重新引发确切的异常还是所有发生的异常?
    • 我想重新引发所有异常以及确切的异常,但这所有这些异常都是相同类型的。请让我知道哪种方法最好。
    • 问题在于,当引发异常时,应用程序会从调用堆栈上一层退出。因此,要从生成的堆栈中抛出另一个异常,您应该重新输入该函数,这是完全不可能的。但还有另一种方式。您可以通过创建聚合异常(如 MyExceptionsCollection)来聚合异常,该异常将保存数组并引发它而不是打印。选择哪一种方法取决于您的具体情况以及您希望如何处理它们。
    • 您可以通过创建聚合异常(如 MyExceptionsCollection)来聚合异常,该异常将保存数组并引发它而不是打印。选择哪一种方法取决于您的具体情况以及您希望如何处理它们。您能否建议任何链接以完全理解。因为这是我第一次处理异常
    猜你喜欢
    • 2013-08-22
    • 2019-10-05
    • 1970-01-01
    • 2016-06-16
    • 2013-12-31
    • 2019-06-23
    • 2011-05-14
    • 2016-11-14
    • 2018-01-04
    相关资源
    最近更新 更多