【问题标题】:Is it a good pattern to raise exceptions in a python decorator?在 python 装饰器中引发异常是一个很好的模式吗?
【发布时间】:2015-03-27 20:55:27
【问题描述】:

上下文:我为不同的 API 端点定义了 Flask 路由,每个端点调用具有某些参数(uid、project_id 等)的控制器类。

@app.route('/sample/route', methods=['POST'])
@require_json_payload
@require_fields({
    'pid',
    'params'
})
def route_handler(arg1, arg2):
    #input filtering
    ...

    try:
        proj_cntr.sample_method(
            pid         = pid,
            ...         = ...
        )
    except ProjCntrException:
        #handle error

    #response generation
    ...

控制器(proj_cntr)负责确定给定的PID是否有效,是否允许给定的用户执行操作,以及其他业务逻辑验证。

我注意到我在不同的控制器中 c/粘贴了很多这样的代码:

if not project_object:
    sys_logger.info('...')
    raise ProjCntrException('PID %d does not exist' % pid)

将这些检查(验证)放在装饰器中似乎是最好的做法。但是如果验证不通过,我不确定哪种错误处理模式是最佳实践。

1) 我是否应该为每个装饰器创建特定的自定义异常(InvalidProjException、PermissionsException 等)以引发?

问题:调用方法的 catch 块会显得臃肿。另外,假设调用者知道被调用者的装饰器引发了哪些异常是不是很好?

2) 装饰器将额外的错误参数传递给方法,方法决定引发什么异常。这样调用者方法就知道期望和处理什么异常类型。

担忧:方法似乎有点过度设计和混乱。

抱歉这个冗长的问题。非常感谢任何想法/想法。

【问题讨论】:

    标签: python exception error-handling flask


    【解决方案1】:

    选项 1:为您的装饰器创建一个特定的 Exception 似乎是合乎逻辑的,因为这将帮助用户了解引发了哪个异常(如果有的话)以及原因。当然,异常的类型应该出现在装饰器文档字符串中,以便调用者能够知道会发生什么。这样,您就不会假设用户知道引发了什么异常,而是假设他已经阅读了文档——这更有可能是假设。但是,如果经常调用该方法,则调用者每次都必须处理它。当使用库、API 或其他人的代码时,这是交易的一部分。

    选项 2:不属于用户来确定将引发哪个异常。例如,用户应该通过文档了解它。就 API 设计而言,它是混乱且反直觉的。

    考虑到您的问题,这让我想到了 Java 的工作方式,尤其是如果您使用 IDE,在调用方法时,您会被 IDE 和通过键入机制警告您应该 try/catch 给定的异常类型。

    但在您的情况下,不可能通过特定消息提出正确的HTTPException 吗?

    【讨论】:

    • 毫无疑问,我最终将尽可能多的信息放入装饰器和装饰方法的文档字符串中。
    【解决方案2】:

    我最终使用了装饰器并在其中抛出了特定的异常。例如:

    @validate_pid 装饰器 raises InvalidPidException() 被捕获在调用装饰方法的任何消费者的 except 块中。

    目前的优势:

    • 控制器更简洁,代码复制更少。
    • 相当通用的解决方案,因为我在我的代码中使用了这些“业务逻辑验证”装饰器。

    目前的缺点:

    • 装饰器依赖于在调用装饰方法时传递的某些键名参数,但其中一些参数并未在方法本身中使用。这可能会导致方法签名中出现一些奇怪的“悬空”参数。
    • 我有一些小的性能问题。例如:项目验证装饰器初始化一个会话并加载一个对象(项目),只是为了在装饰方法本身中再次加载。只是看起来有点笨拙。

    【讨论】:

      猜你喜欢
      • 2011-02-17
      • 2011-07-20
      • 1970-01-01
      • 2010-12-03
      • 2014-02-17
      • 2011-12-28
      • 2017-03-28
      • 1970-01-01
      • 2014-07-07
      相关资源
      最近更新 更多