我应该针对Python中的错误/非法参数组合引发哪个异常?


542

我想知道在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-是否众所周知每个人都将BadValueError在Python中创建自己的s还是存在另一种首选方法?

Answers:


608

我只会提出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相同,那么为什么不使用它呢?


65
>“那么为什么不使用它呢?” -专一性。也许我想抓住一些外层“ MyValueError”,而不是任何/所有“ ValueError”。
凯文·利特尔,

7
是的,所以特异性问题的一部分是在其他地方引发ValueError。如果被调用方函数喜欢您的参数,但在内部调用math.sqrt(-1),则调用方可能会捕捉到ValueError,因为它的参数不合适。也许您只是在这种情况下检查了消息...
cdleary

3
我不确定该参数是否成立:如果有人在调用math.sqrt(-1),那是一个编程错误,无论如何都需要修复。ValueError不打算在正常的程序执行中陷入困境,或从中衍生出来RuntimeError
ereOn

2
如果错误是在NUMBER个参数上,对于一个具有可变数量的参数的函数...例如,其中参数必须为偶数个参数的函数,则应该引发TypeError以便保持一致。并且不要创建自己的类,除非a)有用例或b)导出要供他人使用的库。过早的功能就是代码的死亡。
埃里克·阿隆斯蒂2013年

104

我会继承 ValueError

class IllegalArgumentError(ValueError):
    pass

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

如果需要捕获该特定错误,请使用一个名称。



40
@HamishGrubijan视频很糟糕。当有人建议很好地使用班级时,他大喊“不要使用班级”。辉煌。上课很好。但是不要相信我
罗伯·格兰特

12
@RobertGrant不,你不明白。该视频并不是真的“不使用类”。这是关于不要使事情过于复杂。
RayLuo

15
@RayLuo您可能已经理智地检查了视频的内容,然后将其转换为可口的,有意义的替代消息,但这就是视频的内容,这是没有太多经验和常识的人会消失的地方用。
罗布·格兰特

3
就像我说的那样,@ SamuelSantana,任何时候任何人举手说“ X怎么办?” 在X是个好主意的地方,他只是说,“别再上另一个课了。” 很清楚 我同意关键是平衡。问题是太模糊了,以至于无法实际生活:-)
Rob Grant

18

我认为处理此问题的最佳方法是python本身处理它的方法。Python引发TypeError。例如:

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

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


8
没什么让我感到惊讶的,但是我100%同意,如果传递给函数的某些参数的类型错误,则TypeError是正确的异常。如果变量的类型正确,但其内容和值没有意义,则可以使用ValueError。
user3504575 '19

我认为这可能是因为缺少参数或未要求使用参数,而问题是关于正确给出的参数,但在涉及给定参数的更高抽象级别上是不正确的。但是,正如我实际上在寻找前者一样,无论如何都要投票。
没人

2
如@ user3504575和@Nobody所说,如果参数与函数签名不匹配(位置参数数量错误,关键字参数名称错误,参数类型错误),则使用TypeError,但在函数调用时使用ValueError匹配签名,但参数值无效(例如,调用int('a'))。来源
goodmami

由于OP的问题是指“无效的参数组合”,因此TypeError似乎是适当的,因为在这种情况下,函数签名对于传递的参数本质上是错误的。
J骨头

您的示例调用sum()了不带参数的a TypeError,但是当参数类型正确时,OP担心参数值的“非法”组合。在这种情况下,save和和recurse都是布尔值,但如果recurseis True则不save应该False。这是一个ValueError。我同意对问题标题的某种解释将由回答TypeError,但不针对所提供的示例。
goodmami,


8

这取决于参数的问题。

如果参数的类型错误,则引发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票)。
小清庞-明日香贤治-

-1

我不确定我是否同意继承ValueError-我对文档的解释ValueError应由内建函数引发...从中继承或自己引发它似乎不正确。

当内置操作或函数接收到类型正确但值不合适的参数时引发,并且这种情况没有通过诸如IndexError之类的更精确的异常描述。

- ValueError异常文档


比较google.com/codesearch?q=lang:python+class \ + \ w 错误(([[^ E] \ w * | E [^ x] \ w)):与google.com/codesearch?q=lang: python + class \ + \ w * Error(Exception):
Markus Jarderot

13
这种模糊仅表示内置组件可以提高它,而不仅仅是内置组件可以提高它。在这种情况下,Python文档谈论外部库引发什么并不完全合适。
伊格纳西奥·巴斯克斯

5
我见过的每一个Python软件都曾使用ValueError过这种工具,所以我认为您正在尝试过多地阅读文档。
James Bennett

6
错误,如果我们要使用Google代码搜索对此进行辩驳:google.com/codesearch? q=lang%3Apython+raise%5C+ValueError#66,300个引发ValueError的案例,包括Zope,xen,Django,Mozilla(和那只是从结果的第一页开始)。如果适合使用内置异常,请使用它
。.– dbr

7
如前所述,文档不明确。应该将其写为“当接收到内置操作或内置函数时引发”或“当接收到函数或内置操作时引发”。当然,无论最初的意图是什么,当前的实践都胜过它(如@dbr所指出的)。因此,应将其重写为第二个变体。
同名的

-1

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

class BadCallError(ValueError):
    pass

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

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

总的来说,我希望Python样式在区分函数的错误输入(调用者的错误)和函数内部的错误结果(我的错误)方面更加犀利。因此,可能还存在BadArgumentError来区分参数中的值错误和本地变量中的值错误。


我会KeyError为找不到的关键字加注(因为缺少的显式关键字在语义上与**kwargs缺少该键的字典在语义上相同)。
考伯特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.