如果我们处于损坏状态,我经常使用python的assert语句检查用户输入并快速失败。我知道当使用-o
(优化)标志的python时,assert会被删除。我个人不会在优化模式下运行我的任何应用程序,但感觉我应该远离断言,只是为了以防万一。
写作感觉要干净得多
assert filename.endswith('.jpg')
大于
if not filename.endswith('.jpg'):
raise RuntimeError
这是断言的有效用例吗?如果没有,python的assert
语句的有效用例是什么?
答案 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!"
这是一个有效的案例,恕我直言:
否则,处理可能性,因为你可以看到它的到来。
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
及其语义是早期语言的遗产。最初创建ASSERT
语句的预期用例是:
free()
是否传递了一个先前从malloc()
获得的指针,以及与它们进行非平凡操作后的内部结构完整性检查等。这在过去的日子里是一件大事,而且仍处于为性能而设计的环境中。这就是C ++ / C#中语义的全部原因:
此外,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。