【问题标题】:Python decorated exception cant be exceptedPython 装饰器异常不能被接受
【发布时间】:2017-09-05 00:34:53
【问题描述】:

我使用装饰器类来装饰异常,但是,似乎无法使用确切的异常类来排除异常。使用 functools.update_wrapper 更新装饰器类也不起作用。

class ClsDecor(object):

    def __init__(self,cls):
        self.cls=cls
        self.counter=0

    def __call__(self,*args):
        self.counter+=1
        return self.cls(*args)

@ClsDecor
class Err(Exception):
    def __init__(self):
      Exception.__init__(self)

try:
    raise Err()
except Err as e:
    print 'catched'
except Exception as e:
    print 'Not catched'

【问题讨论】:

  • 你试过从Exception派生装饰器吗?
  • 谢谢,试过了,不行。
  • @ClsDecor 之后,ErrClsDecor 的一个实例Err() 返回初始 Err 类的实例(其名称被装饰覆盖)。现在Err 和从Err() 返回的实例之间存在明显的类型不匹配。类型其实是Err.cls
  • 谢谢,是的,Err.cls 可以工作,有没有什么包装工具可以解决这个问题?
  • 而且,在默认情况下,当我捕获异常时,它的类型是 main.Err'>.

标签: python exception decorator


【解决方案1】:

在大家的帮助下,我终于得到了这样的解决方案:

    class ErrMetaClass(type):
        def __new__(mcls,cls_name,cls_parents,cls_attr):
            return super(ErrMetaClass,mcls).__new__(mcls, 
                   cls_attr['_cls'].__name__, cls_parents,cls_attr)

    def err_dec(cls):
        class NewErr(Exception):
            __metaclass__ = ErrMetaClass
            _cls=cls
            c=0
            def __init__(self):
                NewErr.c+=1
        return NewErr

    @err_dec
    class Err(Exception):
        def __init__(self):
          Exception.__init__(self)

    try:
        raise Err()
    except Err as e:
        print Err.c
    except Exception as e:
        print type(e)

【讨论】:

    【解决方案2】:

    首先,方法__call__应该是:

    def __call__(self, *args):
        return self.cls(*args)
    

    您的代码将在__call__ 内引发错误并直接将您带到print 'Not catched',这是第一级错误。修复此错误后,您将遇到类型不匹配的二级错误,dhke 的评论中对此进行了很好的解释。基本思想是Err实际上是ClsDecor@ClsDecor之后的类型,但是你在__call__中返回了一个原始类型Err的实例,这显然与except Err不匹配。您可以轻松地使用装饰器来归档您的目的,例如:

    def err_dec(Cls):
        class NewErr(Exception):
            def __init__(self, *args, **kwargs):
                self.err = Cls(*args, **kwargs)
    return NewErr
    
    @err_dec
    class Err(Exception):
        def __init__(self):
            Exception.__init__(self)
    

    已更新评论中的要求:

    def err_dec(Cls):
        class NewErr(Exception):
            c = 0
            def __init__(self, *args, **kwargs):
                NewErr.c = NewErr.c + 1
                self.err = Cls(*args, **kwargs)
            def counter(self):
                return NewErr.c
        return NewErr
    
    @err_dec
    class Err(Exception):
        def __init__(self):
            Exception.__init__(self)
    
    try:
        Err()
        Err()
        raise Err()
    except Err as e:
        print e.counter()
        print 'catched'
    except Exception as e:
        print 'Not catched'
    

    【讨论】:

    • 您可以查看我答案的更新部分 :-) 希望对您有所帮助。
    • 谢谢你,你帮了很多忙,最后我用元类修改了新的类名,使异常与原名匹配。 ^_^
    【解决方案3】:

    问题不在于装修。也许装饰让你感到困惑,但这不是问题。

    您需要了解Err 是什么。装饰器像普通定义一样工作,但是在分配名称之前将装饰器应用于对象。也就是相当于:

    class Err(Exception):
    def __init__(self):
      Exception.__init__(self)
    Err = ClsDecor(Err)
    

    现在Err 是一个ClsDecor 实例,当提升它时,您调用的是实例(而不是类),您将调用ClsDecor.__call__(而不是ClsDecor.__init__)。当您尝试使用参数 (args) 调用 self.cls 时,就会出现故障,但 self.clsErr,它不接受任何其他参数(除了 self)。

    如果您在默认的except 子句中打印了错误,您就会看到这一点。如果你点击了默认子句,打印错误是个好主意,因为这通常是因为你捕获了一个意外的异常:

    except Exception as e:
        print 'Not catched', repr(e)
        raise
    

    或者也许你根本不应该捕捉到它 - 如果没有捕捉到异常并且无论如何都会打印回溯。

    【讨论】:

    • 我试图获取默认的类型信息,除了它是main.Err'>,所以我很困惑,我认为它应该被认为是类型错误
    • @user3011780 不应该,我在尝试时得到了TypeError。您应该考虑的另一件事是根本没有默认的except 子句,通常这意味着您遇到了一个您没有预料到的异常并且您无论如何也不知道如何处理它,然后您通常可以让它传播并导致来自 python 的正常堆栈跟踪错误消息。这显示了发生错误的位置。
    • TypeError 应该是由我之前的帖子的拼写错误 '*args' 到 'args' 引起的。谢谢你的好意建议。
    猜你喜欢
    • 1970-01-01
    • 2019-05-13
    • 2015-09-03
    • 2013-12-31
    • 1970-01-01
    • 2020-01-10
    • 2020-04-02
    • 1970-01-01
    • 2019-10-05
    相关资源
    最近更新 更多