【问题标题】:Which exception should I raise on bad/illegal argument combinations in Python?对于 Python 中的错误/非法参数组合,我应该提出哪个异常?
【发布时间】:2010-09-20 08:25:40
【问题描述】:

我想知道在 Python 中指示无效参数组合的最佳实践。我遇到过一些你有这样的功能的情况:

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

唯一的烦恼是每个包都有自己的,通常略有不同BadValueError。我知道在 Java 中存在 java.lang.IllegalArgumentException——大家都知道每个人都会在 Python 中创建自己的 BadValueErrors 吗?还是有另一种首选的方法?

【问题讨论】:

    标签: python exception arguments


    【解决方案1】:

    除非您需要更具体的例外,否则我只会提出 ValueError

    def import_to_orm(name, save=False, recurse=False):
        if recurse and not save:
            raise ValueError("save must be True if recurse is True")
    

    class BadValueError(ValueError):pass 确实没有意义 - 您的自定义类在使用上与 ValueError 相同,那么为什么不使用它呢?

    【讨论】:

    • > “那为什么不使用它呢?” - 特异性。也许我想捕捉一些外层“MyValueError”,但不是任何/所有“ValueError”。
    • 是的,所以特异性问题的一部分是在哪里引发 ValueError 。如果被调用函数喜欢您的参数,但在内部调用 math.sqrt(-1),则调用者可能会捕获 ValueError 期望 its 参数不合适。也许你只是在这种情况下检查消息......
    • 我不确定这个论点是否成立:如果有人打电话给math.sqrt(-1),那是一个编程错误,无论如何都需要修复。 ValueError 不打算在正常程序执行中被捕获,否则它会派生自 RuntimeError
    • 如果错误出现在参数的数量上,对于具有可变参数数量的函数......例如,参数必须是偶数个参数的函数,那么你应该提出一个TypeError,要保持一致。并且不要创建自己的课程,除非 a)您有一个用例或 b)您正在导出库以供其他人使用。过早的功能是代码的死亡。
    • 在这种情况下是否也可以接受断言,或者是否有特定理由使用 ValueError 代替?
    【解决方案2】:

    我会继承 ValueError

    class IllegalArgumentError(ValueError):
        pass
    

    有时最好创建自己的异常,但要从内置的异常继承,它尽可能接近你想要的。

    如果您需要捕获该特定错误,有一个名称会很有帮助。

    【讨论】:

    • 停止编写类和自定义异常 - pyvideo.org/video/880/stop-writing-classes
    • @HamishGrubijan 那个视频太糟糕了。当有人建议很好地使用类时,他只是咩咩地说“不要使用类”。杰出的。课很好。 But don't take my word for it.
    • @RobertGrant 不,你不明白。该视频并不是真正意义上的“不要使用课程”。这是关于不要使事情过于复杂。
    • @RayLuo 你可能已经理智地检查了视频的内容并将其转换为可口、明智的替代信息,但这就是视频所说的,而且这是一个没有很多东西的人经验和常识都会消失。
    • @SamuelSantana 正如我所说,任何时候有人举手说“X 怎么样?” X 是个好主意,他只是说,“不要再上课了。”很清楚。我同意关键是平衡;问题是这太模糊了,无法实际生活:-)
    【解决方案3】:

    我认为处理这个问题的最好方法是 python 本身处理它的方式。 Python 引发类型错误。例如:

    $ python -c 'print(sum())'
    Traceback (most recent call last):
    File "<string>", line 1, in <module>
    TypeError: sum expected at least 1 arguments, got 0
    

    我们的初级开发人员刚刚在谷歌搜索“python 异常错误参数”时发现了这个页面,我很惊讶自从提出这个问题以来的十年里,从未提出过明显的(对我而言)答案。

    【讨论】:

    • 没有什么让我感到惊讶,但我同意 100% TypeError 是正确的异常,如果传递给函数的某些参数的类型错误。如果变量的类型正确但它们的内容和值没有意义,则 ValueError 将是合适的。
    • 我认为这可能是因为缺少或未调用参数,而问题是关于正确给出的参数,但在涉及 value 的更高抽象级别上是不正确的给定的论点。但因为我实际上是在寻找前者,所以无论如何都要投赞成票。
    • 正如@user3504575 和@Nobody 所说,如果参数与函数签名不匹配(位置参数数量错误,关键字参数名称错误,参数类型错误),则使用TypeError,但是当函数调用与签名匹配但参数值无效(例如,调用int('a'))时使用ValueError。 source
    • 当 OP 的问题提到“无效的参数组合”时,似乎 TypeError 是合适的,因为这将是函数签名对于传递的参数本质上是错误的情况。
    • 您的示例调用不带参数的sum(),即TypeError,但当参数类型正确时,OP 担心参数值的“非法”组合。在这种情况下,saverecurse 都是布尔值,但如果 recurseTrue,那么 save 不应该是 False。这是ValueError。我同意TypeError 会回答对问题标题的某些解释,但不适用于所提供的示例。
    【解决方案4】:

    这取决于参数的问题。

    如果参数的类型错误,则引发 TypeError。例如,当您得到一个字符串而不是其中一个布尔值时。

    if not isinstance(save, bool):
        raise TypeError(f"Argument save must be of type bool, not {type(save)}")
    

    但是请注意,在 Python 中我们很少进行这样的检查。如果这个论点真的是无效的,一些更深层次的函数可能会为我们抱怨。如果我们只检查布尔值,也许一些代码用户稍后会只给它一个字符串,知道非空字符串总是 True。这可能会为他节省演员阵容。

    如果参数具有无效值,则引发 ValueError。这似乎更适合您的情况:

    if recurse and not save:
        raise ValueError("If recurse is True, save should be True too")
    

    或者在这种特定情况下,递归的 True 值意味着保存的 True 值。由于我认为这是从错误中恢复,因此您可能还想在日志中抱怨。

    if recurse and not save:
        logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
        save = True
    

    【讨论】:

    • 我认为这是最准确的答案。这显然被低估了(到目前为止包括我的 7 票)。
    【解决方案5】:

    我大多只是看到在这种情况下使用的内置 ValueError

    【讨论】:

      【解决方案6】:

      在这种情况下,您很可能会使用ValueError(完整的raise ValueError()),但这取决于错误值的类型。例如,如果您创建了一个只允许字符串的函数,而用户输入了一个整数,那么您将改为 TypeError。如果用户输入了错误的输入(意味着它具有正确的类型但它不符合某些条件)Value Error 将是您的最佳选择。 Value 错误也可用于阻止程序发生其他异常,例如,您可以使用ValueError 来停止shell 表单,引发ZeroDivisionError,例如,在此函数中:

      def function(number):
          if not type(number) == int and not type(number) == float:
              raise TypeError("number must be an integer or float")
          if number == 5:
              raise ValueError("number must not be 5")
          else:
              return 10/(5-number)
      

      附:有关 python 内置异常的列表,请转到此处: https://docs.python.org/3/library/exceptions.html(这是官方python数据库)

      【讨论】:

        【解决方案7】:

        我不确定我是否同意从 ValueError 继承——我对文档的解释是,ValueError 应该由内置函数...继承自它或自己举起来好像不对。

        在内置操作或 函数接收一个参数 类型正确但不合适 值,而情况不是 由更精确的异常描述 比如IndexError。

        -- ValueError documentation

        【讨论】:

        • 比较google.com/codesearch?q=lang:python+class\+\wError(([^E]\w*|E[^x]\w)): 与google.com/codesearch?q=lang:python+class\+ \w*错误(异常):
        • 那句话只是意味着内置插件会提升它,而不是只有内置插件可以提升它。在这种情况下,Python 文档不完全适合谈论外部库提出的问题。
        • 我见过的每一个 Python 软件都使用ValueError 来处理这类事情,所以我认为你试图阅读过多的文档。
        • Err,如果我们要使用 Google 代码搜索来论证这一点:google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66,300 个引发 ValueError 的案例,包括 Zope、xen、Django、Mozilla(这只是从第一页结果)。如果适合内置异常,请使用它..
        • 如前所述,文档不明确。它应该写为“在内置操作或内置函数接收时引发”或“在函数或内置操作接收时引发”。当然,无论最初的意图是什么,当前的做法都胜过它(正如@dbr 指出的那样)。所以它应该被重写为第二个变体。
        【解决方案8】:

        同意 Markus 关于推出您自己的异常的建议,但异常的文本应阐明问题出在参数列表中,而不是单个参数值。我建议:

        class BadCallError(ValueError):
            pass
        

        在缺少特定调用所需的关键字参数或参数值单独有效但彼此不一致时使用。当特定参数类型正确但超出范围时,ValueError 仍然正确。

        这不应该是 Python 中的标准例外吗?

        一般来说,我希望 Python 风格能够更清晰地区分函数的错误输入(调用者的错误)和函数中的错误结果(我的错误)。所以可能还会有一个 BadArgumentError 来区分参数中的值错误和局部值错误。

        【讨论】:

        • 我会为找不到关键字提出KeyError(因为缺少的显式关键字在语义上与缺少该键的**kwargs dict 相同)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-28
        • 2012-08-26
        • 1970-01-01
        • 2020-09-01
        • 2018-12-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多