【问题标题】:design of python: why is assert a statement and not a function?python的设计:为什么断言是一个语句而不是一个函数?
【发布时间】:2012-11-15 01:45:30
【问题描述】:

在 Python 中,assert 是一个语句,而不是一个函数。这是一个深思熟虑的决定吗?让assert 成为语句(和保留字)而不是函数有什么好处吗?

根据thedocsassert expression1, expression2扩展为

if __debug__:
    if not expression1: raise AssertionError(expression2)

文档还说“当在编译时请求优化时,当前的代码生成器不会为断言语句发出代码。”在不知道细节的情况下,似乎需要一个特殊情况才能使这成为可能。但是,也可以使用一种特殊情况来优化对 assert() 函数的调用。

如果assert 是一个函数,你可以这样写:

assert(some_long_condition,
       "explanation")

但是因为assert 是一个语句,所以元组的计算结果总是True,并且 你得到

SyntaxWarning: assertion is always true, perhaps remove parentheses?

正确的写法是

assert some_long_condition, \
       "explanation"

这可以说是不那么漂亮了。

【问题讨论】:

  • 这有点令人惊讶,它没有被转换为带有print 的函数。虽然,我不喜欢使用原始的assert 语句。我只在测试代码中使用unittest.TestCase.assertTrue()和朋友,并保持生产代码无断言。
  • 当参数在技术上是有效的但关于它们的一些重要假设(通常是环境的其他部分)不成立时,我会抛出 AssertionError。当然,前提是没有更好的类别。

标签: python language-design assert


【解决方案1】:

将 assert 设为语句(和保留字)而不是函数有什么好处吗?

  1. 不能重新分配给用户函数,这意味着它可以在编译时被有效地禁用,正如@mgilson 指出的那样。
  2. 对第二个可选参数的评估被推迟到如果/当断言失败时。用函数和函数参数来做这件事很尴尬(需要传递一个 lambda)。不推迟对第二个参数的评估会带来额外的开销。

【讨论】:

  • 我不会说“更容易禁用”,因为在 assert 是一个函数的情况下,我认为不可能优化掉:)。
  • @mgilson 你是对的,该死的选举刚刚结束,我一定是说错了。 ;) 但是开个玩笑,是的,你是绝对正确的。
  • 我没有想到你提出的第二点(明白了吗?提出了?因为断言raise AssertionError?)。为此 +1。
  • 只想再补充一点,正如我在另一个答案中提到的那样:它不仅延迟了第二个可选参数。第一个参数也被延迟,因此只有在调试模式下才会对其进行评估
【解决方案2】:

assert 在 python 和其他语言(特别是 C)中的一个奇妙之处在于,您可以删除它们以优化您的代码,只需添加正确的 #define(可选地在命令行上使用任何编译器我'曾经使用过)或优化标志(python 中的-O)。如果assert变成了一个函数,这个特性就不可能添加到python中,因为直到运行时你才知道你是否有内置的assert函数或同名的用户定义函数。


还要注意,在 python 中,函数调用相当昂贵。用代码if __debug__: ... 替换内联可能比执行函数调用更有效,如果将assert 语句放在性能关键例程中可能会很重要。

【讨论】:

  • 我对您关于“(CPython)代码生成器不删除断言语句(当前)”的说法感到困惑。从文档中,如果您打开优化,它似乎确实删除了语句(不为它们发出代码)。我是不是误会了?
  • @cberzan -- 你是对的。直到刚才我才看到您的评论,但看起来 python 确实在打开了适当的优化标志的情况下删除了它们。我已编辑以删除该错误。 (谢谢!)
【解决方案3】:

除了其他答案(有点离题)之外,还有一个提示。为避免使用反斜杠,您可以在括号内使用隐式行连接。 ;-)

代替:

assert some_long_condition, \
       "explanation"

你可以写:

assert some_long_condition, (
       "explanation")

【讨论】:

    【解决方案4】:

    我不是 Python 方面的专家,但我相信性能是最大的原因之一。

    如果我们有 assert(expression, explain) 作为函数,如果表达式的计算成本很高,即使我们处于非调试模式,Python 也需要计算两个表达式才能将其传递给 assert 函数。

    通过扩展断言,表达式和解释语句实际上不会被评估,除非它们确实需要(当 debug 评估为真时)。我认为,如果我们想要在不必要的时候断言不影响性能(即生产系统中没有性能影响),这一点至关重要。

    【讨论】:

    • 如果 debug 评估为 false,为什么很难让函数评估为 nop 或其他东西?
    • @rasen58 我不是说函数本身,而是我们作为断言参数传递的表达式。你不能让它们 noop,因为 Python 不是惰性求值的。当然,您可以通过将表达式评估更改为 lambda 来延迟表达式评估,并且仅当它是非调试时才在函数中评估它。然而,这不是所采取的选择(我相信 assert 比 lambda 更早可用,在这里使用 lambda 可能很冗长)
    猜你喜欢
    • 1970-01-01
    • 2016-08-18
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    相关资源
    最近更新 更多