ChatGPT解决这个技术问题 Extra ChatGPT

对于 Python 中的错误/非法参数组合,我应该提出哪个异常?

我想知道在 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 中创建自己的 BadValueError,或者是否有另一种首选方法?


B
Bonifacio2

我只会提出 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 期望它的参数不合适。也许你只是在这种情况下检查消息......
我不确定这个论点是否成立:如果有人在调用 math.sqrt(-1),那么这是一个无论如何都需要修复的编程错误。 ValueError 不打算在正常程序执行中被捕获,否则它会派生自 RuntimeError
如果错误出现在参数的数量上,对于具有可变参数数量的函数......例如,参数必须是偶数个参数的函数,那么您应该引发 TypeError,以保持一致。并且不要创建自己的类,除非 a)您有一个用例或 b)您正在导出库以供其他人使用。过早的功能是代码的死亡。
在这种情况下,断言是否也可以接受,或者是否有特定的理由使用 ValueError
M
Markus Jarderot

我会从 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 是个好主意,他只是说,“不要再上课了。”很清楚。我同意关键是平衡;问题是这太模糊了,无法实际生活:-)
J
J Bones

我认为处理这个问题的最好方法是 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 将是合适的。
我认为这可能是因为缺少或不需要参数,而问题是关于正确给出的参数,但在涉及给定参数值的更高抽象级别上是不正确的。但由于我实际上是在寻找前者,所以无论如何都要投赞成票。
正如@user3504575 和@Nobody 所说,如果参数与函数签名不匹配(位置参数数量错误、名称错误的关键字参数、参数类型错误),则使用 TypeError,但在函数调用时使用 ValueError匹配签名但参数值无效(例如,调用 int('a'))。 source
由于 OP 的问题提到了“无效的参数组合”,因此 TypeError 似乎是合适的,因为这将是函数签名对于传递的参数本质上是错误的情况。
您的示例调用不带参数的 sum(),即 TypeError,但当参数类型正确时,OP 担心参数值的“非法”组合。在这种情况下,saverecurse 都是布尔值,但如果 recurseTrue,那么 save 不应该是 False。这是一个ValueError。我同意 TypeError 将回答对问题标题的某些解释,但不适用于所提供的示例。
G
Gloweye

这取决于论点的问题所在。

如果参数的类型错误,则引发 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 值意味着 Save 的 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 票)。
E
Eli Courtwright

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


t
thing10

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

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)

PS 对于 python 内置异常的列表,请到这里:https://docs.python.org/3/library/exceptions.html(这是官方 python 数据库)


B
BobHy

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

class BadCallError(ValueError):
    pass

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

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

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


我会为找不到关键字提出 KeyError (因为缺少的显式关键字在语义上与缺少该键的 **kwargs 字典相同)。
c
cdleary

我不确定我是否同意从 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*Error(Exception):
该简介只是意味着内置程序会提高它,而不是只有内置程序才能提高它。在这种情况下,Python 文档不完全适合讨论外部库提出的问题。
我见过的每一个 Python 软件都使用 ValueError 来完成这类事情,所以我认为你试图过多地阅读文档。
错误,如果我们要使用 Google 代码搜索来论证这一点:google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66,300 个引发 ValueError 的案例,包括 Zope、xen、Django、Mozilla(这只是来自结果的第一页)。如果适合内置异常,请使用它..
如前所述,文档是模棱两可的。它应该写成“在内置操作或内置函数接收时引发”或“在函数或内置操作接收时引发”。当然,无论最初的意图是什么,当前的做法都胜过它(正如@dbr 指出的那样)。所以它应该被重写为第二个变体。