【发布时间】:2016-07-21 07:30:40
【问题描述】:
普遍认为使用eval 是不好的做法。对this question 的公认答案表明几乎总是有更好的选择。但是,标准库中的 timeit 模块使用它,我偶然发现了一个找不到更好替代方案的案例。
unittest module 具有以下形式的断言函数
self.assert*(..., msg=None)
允许断言某些东西,如果失败,可以选择打印msg。这允许运行类似的代码
for i in range(1, 20):
self.assertEqual(foo(i), i, str(i) + ' failed')
现在考虑foo 可以引发异常的情况,例如,
def foo(i):
if i % 5 == 0:
raise ValueError()
return i
然后
- 一方面,
msg不会被打印出来,因为从技术上讲,assertEqual从来没有被调用用于有问题的迭代。 - 另一方面,从根本上说,
foo(i) == i不成立(诚然,因为foo(i)从未完成执行),因此在这种情况下,打印msg会很有用。
我想要一个打印出msg 的版本,即使失败原因是异常 - 这将允许准确了解哪个调用失败。使用eval,我可以通过编写一个带有字符串的版本来做到这一点,如下所示(这是一个稍微简化的版本,只是为了说明这一点):
def assertEqual(lhs, rhs, msg=None):
try:
lhs_val = eval(lhs)
rhs_val = eval(rhs)
if lhs_val != rhs_val:
raise ValueError()
except:
if msg is not None:
print msg
raise
然后使用
for i in range(1, 20):
self.assertEqual('foo(i)', 'i', str(i) + ' failed')
当然,从技术上讲,可以通过将每个对assert* 的调用放在try/except/finally 中来实现完全不同的操作,但我只能想到极其冗长的替代方案(这也需要复制msg。)
那么,eval 的使用在这里合法吗,还是有更好的选择?
【问题讨论】:
-
您这样做是为了解决什么问题?
-
@snakecharmerb 正如问题中所述,在“这允许运行类似代码”之后运行 sn-p,并在失败的迭代中打印出
msg。 -
当然,但这比获取现有行为更好吗?我看不出这比仅仅阅读你免费获得的堆栈跟踪和异常消息有什么好处。
-
@snakecharmerb IINM,堆栈跟踪不会显示在哪个迭代中引发了异常,不是吗?
-
确实如此。你需要stackoverflow.com/a/34094/5320906之类的东西吗?
标签: python exception eval python-unittest