Is it bad practice to use assert to verify assumptions about arguments in python?
我正在编写一个类,其__init__使用id或slug参数,但不是两者都使用。 我想验证参数是否符合预期。 将assert用于验证关于参数的假设的特定目的是否合适和良好实践,或者如果参数不符合预期,我是否应该引发异常?
例如。,
1 2
| def __init__(self, id=None, slug=None):
assert((id or slug) and not (id and slug)) |
-
如果失败,assert将引发异常...你可以添加一个逗号,然后添加一条将附加到异常的消息,例如assert 1==2,"1 is not equal to 2"
-
优化时会删除断言。请参阅此相关问题的答案:stackoverflow.com/a/1838411/303748
-
Joran,是的,我意识到这一点。不过,这条消息很好,我会加一个。
-
我不得不问......如果你能接受id或slug中的一个,为什么这不是__init__(self, id_or_slug)?然后你只能通过其中一个。
-
cHao,你说得好。我没有想到这一点,但我觉得它可以采取任何一种方式。如果它的id_or_slug,我将不得不检查参数的值,以确定它是id还是slug,因为进行数据库查找的代码根据它的不同而不同。但鉴于你的建议,我会以这种方式尝试,看看我喜欢它。它似乎提供了更清晰的界面,并且无需进行复杂的输入验证或额外的文档。事实上,我对问题的主题本身更感兴趣,而不是如何最好地编写我的代码,但这有所帮助。
-
我可以建议这个等同的陈述吗? assert(bool(id) != bool(slug))。对不起,非最小化的逻辑让我感到困扰,因为我参加了一个课程,我们花了整整一个月的时间研究它。
-
你可以建议,但我不会用它。我明白了,但它并没有传达我所说的特别好的东西(即,它的可读性较差)。我参加了一个课程,我们花了整整一个学期研究编译器,最小化逻辑是他们擅长的。"可读性很重要。" - Python的禅宗
-
@BenRoberts我绝对同意你的说法,你的版本更具可读性,虽然乍一看还不是很清晰。顺便说一句,如果您在评论开头添加@然后是您要回复的用户的名称,它会自动通知该用户您已做出回复。
-
可能与Python Assert的最佳实践重复
-
关闭?拜托了伙计们。是否禁止在SO上讨论"最佳实践"?因为它绝对不是由FallenAngel链接的东西的复制品(顺便提一下,这是最佳实践)。我觉得这是一个合法的,有建设性的问题,这个问题得到了回答,我从中学到了很有价值的东西。你们中的任何人都可以在线下联系我,并建议我如何改进问题,重新打开或向我解释我是如何离开基地的。
-
@Lanaru感谢@ tip。 (不知道我之前从未接受过这种情况。)我改变了主意,并且最小化了,因为如果测试结果为True,我会提出一个ValueError,其中包含一条消息,说明测试是什么所有这些,最大限度地减少了测试本身的可读性较差。
原始问题的答案("使用断言来验证python中的内部假设是不好的做法?")绝对没有。这是每个人都同意断言有益的一件事。但是你在问题中描述的不是内部假设的验证:它是对外部输入的验证。一个TypeError,一个ValueError,另一个内置异常(或者可能是一个自定义异常,尽管应该保守一个)给API的用户提供了更有用的反馈。更糟糕的是,断言可能(并且,在某些环境中,通常是)被完全删除,这意味着您的代码将默默地做错事。
-
这个问题的概要很好。但是,我想我会对内部假设和"外部"输入验证之间的差异感到困惑。如果其他代码(暂时在我自己的代码库中)可能通过错误地实例化类(即,它不能由软件的用户触发)来触发这个,那么这是内部假设还是外部输入?
-
@BenRoberts:就你的功能而言,它是外部输入。你不希望你的函数对彼此做出太多假设。如果随机用户要输入它,就像验证一样。
-
ValueError也是一个重要的候选者,并且(在问题中的示例的上下文中)是优选的选择。
-
@JonasWielicki我比这里的RuntimeError更喜欢它,但我不认为它适用于此。也许我的解释方式不同,但对我而言,这表示无效的值,而不是错误的数值。
-
是的,这很难,但我会提出一个值错误,因为我想到的错误信息有点完全是foo和bar之一,不能是None,这对我来说是ValueError'ish。对我来说,关键字参数在invalid-argument-count TypeError的意义上是不可数的。
-
@delnan(以及其他任何人)我对我的问题进行了一些编辑,以使我更清楚我所要求的内容,并且因为它被关闭而感到有点困扰。如果你认为封闭是合法的,你可以投票重新开放或让我知道吗?
-
我投票重新开放,我认为这是一个合法而且完全可以回答的问题。 然后,我可能会有偏见。
如果这是关于代码健全性的,正如您所描述的那样,断言是正确的使用方法。如果这是关于输入验证,则不应使用assert。特别是,用户不应该通过向其提供垃圾来触发断言。断言只能由于程序中的错误而触发。
编辑:正如我刚刚从评论中学到的,Python还允许您完全关闭断言(意味着它们不会被评估,因此不会失败)。这应该是正确的答案(除了使用像汇编或C这样的不同语言)到性能问题,如果有的话。
-
第二段对我没有意义。无论如何,您需要检查条件,如果这样做,断言至少同样快,如果不是更快(如果在编译时禁用)。
-
@delnan:如果你真的需要每次检查条件,那么你不应该使用断言。它们是一种健全性检查,而不是输入验证工具。
-
如果您知道使用正确的参数调用函数,则无需进行检查。我同意无论如何检查它是有用的,但如果性能是一个真正的问题,人们可能想要省略检查。然后,正如我所写,这可能只是C和汇编程序员的一个问题。
-
@cHao每次执行时都会检查一个断言(条件相同)。这不是断言与if ...: raise ...的区别。此外,这不是答案所说的:它说断言比替代方案慢(无论如何,我必须在这里猜测)。哪个我真的看不到发生的事情。
-
虽然这篇文章确实说得对,但我对upvote感到有点不舒服,因为它在某种程度上感觉像是"是的,但是"在一个应该回答"否"的问题上。你会在问题中给出一些关于具体例子的句子吗?这使它成为一个真正令人振奋的答案。
-
@delnan的区别在于,在python和其他方面,可以关闭断言以获得性能优势,因此省略了检查。除了语义之外,这就是它们与if …: raise …的真正区别。
-
@delnan:你自己的答案说断言并不总是被检查。
-
@JonasWielicki我完全知道(我已经提到过三次了)。这就是断言如何更快,而不是更慢。
-
@cHao是的,我打算写"如果它没有被-O禁用,在这种情况下它是免费的"。
-
我对你的评论感到困惑"每次执行时都会检查断言(条件相同)。那是[...]"那么,抱歉。不想让你精力充沛。
-
@delnan:好的,既然我实际上已经获得了你的帖子,我必须同意;性能论证有点BS。 (如果断言花费太多,那么在生产中关闭它们 - 无论如何你应该能够在没有它们的情况下运行。)如果第二段完全被省略,那么剩下的就值得投票了。
-
@jonas:替代方案是根本没有检查。如果程序正确,则可以关闭或省略断言。我不知道Python有一个选项可以关闭断言。如果性能是一个问题,这将是那样的方式。
失败时,Assert会引发AssertionError。当参数不正确时,python标准库经常引发ValueError - 当你使用错误数量的参数调用函数时,或者TypeError:
考虑:
1 2
| int("15f") #ValueError
int("15",2,3) #TypeError |
你需要问自己的问题(从API的角度来看)哪个是最合乎逻辑的,你的函数可以提出错误的输入?无论您选择什么,请确保将其记录得很好,这样您的用户就可以知道如何处理错误。