【问题标题】:exception handling in decorator function装饰器函数中的异常处理
【发布时间】:2016-12-14 14:26:41
【问题描述】:

Python 新手,我有一堆函数可以在某些硬件上执行各种任务。每个函数都有不同数量的参数和返回值。

我想制作一种通用的“重试”包装函数,它会从我的任何函数中捕获异常并进行一些错误处理(例如重试任务)。

据我了解,我应该能够将装饰器函数用作每个函数的通用包装器。这似乎可行,但我似乎无法真正从装饰器函数中调用的函数中获得任何异常。

我查看了各种示例并想出了这个:

def retry(function):
    def _retry(*args, **kwargs):
        try:
            reply = function(*args, **kwargs)
            print "reply: ", reply
            return reply
        except PDError as msg:
            print "_retry", msg
        except:
            print "_retry: another error"
    return _retry

然后我使用我的一个函数的名称来调用它:

value = retry(pd.command_get_parameter(0x00))

它似乎调用了我的函数并正确返回,但我的重试函数中从未捕获到异常。所以我无法处理错误并重试。

我也试过这个:

from functools import wraps

def retry(function):
    @wraps(function)
    def _retry(*args, **kwargs):
    .....

我不确定我做错了什么,或者这是否是最好的方法。有人对如何做到这一点有建议吗?我真的不想为我的每个主要功能分别创建“重试”功能。

【问题讨论】:

  • 您的意思是retry(pd.command_get_parameter)(0x00)?现在您将pd.command_get_parameter(0x00) 的结果作为function 传递给retry。
  • @Will:你应该像这样使用:@retry def command_get_parameter(..)。您还可以传入 retry(command_get_parameter) 之类的函数,并在其位置使用返回的函数。你没有做它们中的任何一个。您正在从 command_get_parameter(..) 传递返回值以重试 - 您应该在其中传递一个函数。
  • @Ryan:谢谢。现在你已经解释过了,我可以明白为什么它在我传递结果时不起作用。

标签: python python-2.7 exception decorator


【解决方案1】:

将我的评论转换为答案:

你应该像这样使用:

def retry(function):
    @wraps(function)
    def _retry(*args, **kwargs):
        try:
            reply = function(*args, **kwargs)
            print "reply: ", reply
            return reply
        except PDError as msg:
            print "_retry", msg
        except:
            print "_retry: another error"
    return _retry

class SomeClass(object):

    @retry
    def command_get_parameter(..):
        return <some value>
s = SomeClass()
result = s.command_get_parameter(..)  #retry(..) actually invokes this function. 

装饰器接收一个函数,并返回一个装饰函数。装饰是能够在函数被调用之前、之后或捕获异常等之前做某事的东西。如果你使用上面的语法(@retry),解释器调用retry(..),传入函数对象(@987654324 @),并将函数替换为retry(command_get_parameter)返回的函数。

发生了什么有点类似于以下步骤(伪代码):

new_command_get_parameter = retry(command_get_parameter) #@retry has this effect.
result = new_command_get_parameter(your_input)

不同之处在于,以上两个步骤是由解释器神奇地为您完成的——保持代码更清晰、更易读。


目前您正在调用该函数,并将其结果传递给retry(..),这显然是错误的。此外,它不会按照您希望的方式捕获异常。


更新:如果要重试访问实例变量,只需让_retry使用第一个参数为self即可。比如:

def retry(func):
    def _retry(self, *args, **kwargs):
        print "Decorator printing a:", self.a
        print "Decorator printing b:", self.b
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print "Caught exception"
            return "Grr.."
    return _retry


class Temp(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @retry
    def command(self, *args, **kwargs):
        print "In command."
        print "Args:", args
        print "KWargs:", kwargs

        raise Exception("DIE!")

t = Temp(3, 5)

print t.command(3,4,5, a=4, b=8)

输出:

Decorator printing a: 3
Decorator printing b: 5
In command.
Args: (4, 5)
KWargs: {'a': 4, 'b': 8}
Caught exception
Grr..

【讨论】:

  • 感谢您的回复。因此,如果我要在您的示例中将“重试”作为“SomeClass”的一部分,我该如何定义它?例如: class SomeClass(object): @retry def retry(self, function): try: reply = function(*args, **kwargs) return reply except: #handle error Do I need @wraps(function)
  • 我认为我对上一个问题不太清楚。是否可以将重试功能作为 SomeClass 类的一部分?例如,如果作为重试机制的一部分,我需要在 SomeClass 中调用不同的函数,我就需要这个。这是我尝试过但无效的方法:code class SomeClass(object): def retry(self, function): @wraps(function) def _retry(*args, **kwargs): try: reply = function (*args, **kwargs)
  • @Will: 不在同一个类,因为1)解释器需要定义重试函数(因为它定义后马上执行。2)retry函数不可用直到解释器完成对类的解析。我会用你能得到的最接近的答案更新我的答案。
  • 很好的解释。谢谢。
猜你喜欢
  • 2020-01-10
  • 2019-05-13
  • 2017-09-20
  • 1970-01-01
  • 2016-09-27
  • 2022-01-05
  • 2016-01-30
  • 1970-01-01
  • 2012-03-27
相关资源
最近更新 更多