python's timeit 未在设置代码中初始化列表

python's timeit is not initialising list in setup code

我正在从 python setlist 中删除计时元素。我的列表 timeit 代码正在引发 ValueError: ... x not in list,但只有当我 运行 多次迭代 timeit !??

看来对于list来说,setup代码中创建的变量在后续的迭代中被重新使用了(好像setup代码不是运行第二次??)。

这是我的代码:

In [1]: import timeit

In [2]: timeit.timeit(stmt='a.discard(10**5)', setup='a = set(range(10**6))', number=100000)
Out[2]: 0.02187999989837408

In [3]: timeit.timeit(stmt='a.remove(10**5)', setup='a = list(range(10**6))', number=1)
Out[3]: 0.023419374600052834

In [4]: timeit.timeit(stmt='a.remove(10**5)', setup='a = list(range(10**6))', number=2)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
...
ValueError: list.remove(x): x not in list

怎么回事??

In [4]: timeit.timeit(stmt='a.remove(10**5)', setup='a = list(range(10**6))', number=2)

a.remove(10**5) 被执行了两次但是 10**5 只能被删除一次。 setup 仅调用一次,因此您始终在同一个列表或集合上工作。

这相当于

a = list(range(10**6))
a.remove(10**5)  # works
a.remove(10**5)  # fails with ValueError: list.remove(x): x not in list

这不会对集合失败,因为集合丢弃和列表删除的行为不同。 如果你使用 set remove 方法,你最终也会得到一个错误 (KeyError)

a = set(range(10**6))
a.discard(10**5)  # works
a.discard(10**5)  # works

但是

a = set(range(10**6))
a.remove(10**5)  # works
a.remove(10**5)  # KeyError 10**5

如果要丢弃的元素不在集合中,则对集合的 discard() 操作不执行任何操作。但是,当元素不存在时,列表上的 remove() 操作会引发 ValueError。当您使用 range(10**6) 初始化您的列表时,值 10**5 只出现一次;在集合中这不是问题,但在列表中它只能被删除一次并且会在以后尝试删除相同值时出错。

他们的关键点是 setup 仅执行 一次 即使 number 大于 1(因此 ValueError 当你尝试使用已从列表中删除的唯一值调用 list.remove)。来自 docs(强调我的):

Time number executions of the main statement. This executes the setup statement once, and then returns the time it takes to execute the main statement a number of times, measured in seconds as a float. The argument is the number of times through the loop, defaulting to one million. The main statement, the setup statement and the timer function to be used are passed to the constructor.

所以,如果你想像这样对一个代码片段进行多次计时(比如获得更准确的计时),那么你必须使用 number=1,但你可以使用 repeat 参数用 timeit.repeat() 代替:

>>> timeit.repeat(stmt='a.remove(10**5)', setup='a = list(range(10**6))', number=1, repeat=2)
[0.002321417909115553, 0.0023121219128370285]