python的`assert`语句有哪些可接受的用例?

时间:2010-01-26 19:58:59

标签: python assert

如果我们处于损坏状态,我经常使用python的assert语句检查用户输入并快速失败。我知道当使用-o(优化)标志的python时,assert会被删除。我个人不会在优化模式下运行我的任何应用程序,但感觉我应该远离断言,只是为了以防万一。

写作感觉要干净得多

assert filename.endswith('.jpg')

大于

if not filename.endswith('.jpg'):
    raise RuntimeError

这是断言的有效用例吗?如果没有,python的assert语句的有效用例是什么?

8 个答案:

答案 0 :(得分:26)

断言应用于表达不变量前置条件
在您的示例中,您使用它们来检查意外输入 - 这是一个完全不同的异常。

根据要求,在错误的输入上引发异常并停止应用程序可能完全可以;但是,代码应该始终为表达性而量身定制,而AssertionError的提升并不明确 更好的是提出你自己的例外,或ValueError

答案 1 :(得分:18)

如果优雅是不可能的,那就是戏剧性的

以下是您的代码的正确版本:

if filename.endswith('.jpg'):
    # convert it to the PNG we want
    try:
        filename = convert_jpg_to_png_tmpfile(filename)
    except PNGCreateError:
        # Tell the user their jpg was crap
        print "Your jpg was crap!"

这是一个有效的案例,恕我直言:

  1. 错误完全,100%致命,处理它将太难以理解
  2. 如果导致逻辑规律发生变化
  3. ,则断言应该失败

    否则,处理可能性,因为你可以看到它的到来。

    ASSERT ==“此应该永远不会发生,如果确实如此,我们会放弃”

    当然,这与

    不一样
    #control should never get here
    

    但我总是这样做

    #control should never get here
    #but i'm not 100% putting my money where my mouth
    #is
    assert(False)
    

    这样我得到了一个很好的错误。在您的示例中,我将使用if版本并将文件转换为jpg!

答案 2 :(得分:9)

完全有效。断言是关于你的程序状态的正式声明。

你应该将它们用于无法证实的事物。但是,它们对于您认为的事物非常方便,您可以证明它们是对您逻辑的检查。

另一个例子。

def fastExp( a, b ):
    assert isinstance(b,(int,long)), "This algorithm raises to an integer power"
    etc.

还有一个。最后的断言有点愚蠢,因为它应该是可证明的。

# Not provable, essential.
assert len(someList) > 0, "Can't work with an empty list."
x = someList[]
while len(x) != 0:
    startingSize= len(x)
    ... some processing ...
    # Provable.  May be Redundant.
    assert len(x) < startingSize, "Design Flaw of the worst kind."

还有一个。

def sqrt( x ):
    # This may not be provable and is essential.
    assert x >= 0, "Can't cope with non-positive numbers"
    ...
    # This is provable and may be redundant.
    assert abs( n*n - x ) < 0.00001 
    return n

制作正式断言有很多理由。

答案 3 :(得分:6)

assert最适合在测试期间应该处于活动状态的代码,当您确定无需-o时运行

您个人永远不会与-o一起运行,但如果您的代码在较大的系统中结束并且管理员想要使用-o运行它,会发生什么?

系统可能看起来运行正常,但是通过-o

运行已经打开了一些微妙的错误

答案 4 :(得分:4)

就我个人而言,我使用assert来表示意外错误,或者您认为在实际使用中不会发生的事情。每当处理来自用户或文件的输入时都应该使用例外,因为它们可以被捕获并且您可以告诉用户“嘿,我期待一个.jpg文件!!”

答案 5 :(得分:2)

Python Wiki有一个great guide有效地使用Assertion。

上面的答案在运行Python时没有必要澄清-O异议。 引用上面的页面:

  

如果使用-O选项启动Python,则断言将被删除而不进行评估。

答案 6 :(得分:1)

S.Lott的答案是最好的答案。但这太长了,只能添加对他的评论,所以我把它放在这里。无论如何,这是我对assert的看法,基本上它只是一种做#ifdef DEBUG的简写方式。

无论如何,关于输入检查有两种思想流派。您可以在目标位置执行此操作,也可以在源位置执行此操作。

在目标处执行此操作是在代码中:

def sqrt(x):
    if x<0:
        raise ValueError, 'sqrt requires positive numbers'
    root = <do stuff>
    return root

def some_func(x):
    y = float(raw_input('Type a number:'))
    try:
        print 'Square root is %f'%sqrt(y)
    except ValueError:
        # User did not type valid input
        print '%f must be a positive number!'%y

现在,这有很多优点。也许,写sqrt的人最了解他的算法的有效值是什么。在上面,我不知道我从用户获得的值是否有效。有人必须检查它,并且在知道最有效的代码 - sqrt算法本身的代码中进行检查是有意义的。

但是,会有性能损失。想象一下这样的代码:

def sqrt(x):
    if x<=0:
        raise ValueError, 'sqrt requires positive numbers'
    root = <do stuff>
    return root

def some_func(maxx=100000):
    all_sqrts = [sqrt(x) for x in range(maxx)]
    i = sqrt(-1.0)
    return(all_sqrts)

现在,此函数将调用sqrt 100k次。每次,sqrt都会检查值是否> = 0.但我们已经知道它是有效的,因为我们如何生成这些数字 - 那些额外的有效检查只是浪费执行时间。摆脱它们不是很好吗?然后有一个会抛出一个ValueError,所以我们会抓住它,并意识到我们犯了一个错误。我依靠子功能编写我的程序来检查我,所以我只是担心当它不起作用时恢复。

第二种思路是,不是对目标函数检查输入,而是为定义添加约束,并要求调用者确保它使用有效数据进行调用。该功能承诺,凭借良好的数据,它将返回其合同所说的内容。这避免了所有这些检查,因为调用者比目标函数知道更多关于它发送的数据,它来自哪里以及它本身具有什么约束。这些的最终结果是代码契约和类似的结构,但最初这只是按惯例,即在设计中,如下面的评论:

# This function valid if x > 0
def sqrt(x):
    root = <do stuff>
    return root

def long_function(maxx=100000):
    # This is a valid function call - every x i pass to sqrt is valid
    sqrtlist1 = [sqrt(x) for x in range(maxx)]
    # This one is a program error - calling function with incorrect arguments
    # But one that can't be statically determined
    # It will throw an exception somewhere in the sqrt code above
    i = sqrt(-1.0)

当然,错误发生了,合同可能会违规。但到目前为止,结果大致相同 - 在这两种情况下,如果我调用sqrt(-1.0),我将在sqrt代码本身内得到一个异常,可以走向异常堆栈,并找出我的错误所在。

然而,还有更多阴险的情况...例如,假设我的代码生成列表索引,存储它,稍后查找列表索引,提取值,并进行一些处理。并且假设我们偶然得到-1列表索引。所有这些步骤实际上可以完成而没有任何错误,但我们在测试结束时有错误的数据,我们不知道为什么。

为什么断言?在我们测试和证明我们的合同时,有一些我们可以接近失败的调试信息的东西会很好。这与第一种形式几乎完全相同 - 毕竟,它正在进行完全相同的比较,但它在语法上更整洁,并且在验证合同时更专业。另一个好处是,一旦您有理由确定您的程序正常运行,并且正在优化并寻求更高的性能与可调试性,那么所有这些现在冗余的检查都可以被删除。

答案 7 :(得分:-1)

Botton line:

  • assert及其语义是早期语言的遗产。
  • Python的重点转移已被证明使传统用例无关紧要。
  • 截至本文撰写时,没有其他用例被正式提出,但有一些想法可以将其改为通用检查。
  • 如果您对这些限制感到满意,可以使用(并且确实使用)作为通用检查。

最初创建ASSERT语句的预期用例是:

  • 进行相关但仍被视为过于昂贵或无需生产使用的完整性检查。
    • E.g。这些可以是内部函数的输入检查,检查free()是否传递了一个先前从malloc()获得的指针,以及与它们进行非平凡操作后的内部结构完整性检查等。

这在过去的日子里是一件大事,而且仍处于为性能而设计的环境中。这就是C ++ / C#中语义的全部原因:

  1. 在发布版本中删除
  2. 立即终止程序,尽可能不大声地播放程序。
  3. 然而,Python有意识地和有意地牺牲了程序员性能的代码性能(不管你信不信,我最近通过将一些代码从Python移植到Cython来获得100倍的加速 - 甚至没有禁用边界检查!)。 Python代码运行在&#34; safe&#34;环境,你不能完全&#34;打破&#34;你的进程(或整个系统)到一个无法追踪的段错/ BSoD / bricking - 你得到的最糟糕的是一个未处理的异常,附加了大量的调试信息,所有这些都以可读的形式呈现给你。

    • 这意味着运行时和库代码应该在适当的时刻包括所有类型的检查 - 在任何时候,它是否&#34;调试模式&#34;或不。

    此外,Python对提供源代码的影响很大(透明编译,追溯源代码行,股票调试器期望它们与.pyc一起使用是有用的)非常模糊&#34;开发&#34;之间的界限。和&#34;使用&#34; (这是setuptools&#39;自包含.egg s created a backlash - and why pip always installs them unpacked的一个原因:如果安装了一个,则源不再是容易获得和有问题 - 可诊断)。

    组合起来,这些属性几乎都会破坏&#34;仅调试&#34;代码。

    并且,你猜对了,an idea about repurposing assert as a general-purpose check eventually surfaced