Python timeit模块执行混乱

Cra*_*arB 6 python timeit python-3.x

我正在尝试使用Python中的timeit模块(编辑:我们使用的是Python 3)来决定几个不同的代码流.在我们的代码中,我们有一系列if语句来测试字符串中是否存在字符代码,如果有,则将其替换为:

if "<substring>" in str_var:
    str_var = str_var.replace("<substring>", "<new_substring>")
Run Code Online (Sandbox Code Playgroud)

对于不同的子串,我们这样做了很多次.我们在这之间进行辩论并使用像这样的替换:

str_var = str_var.replace("<substring>", "<new_substring>")
Run Code Online (Sandbox Code Playgroud)

我们尝试使用timeit来确定哪一个更快.如果上面的第一个代码块是"stmt1"而第二个是"stmt2",我们的设置字符串看起来像

str_var = '<string><substring><more_string>',
Run Code Online (Sandbox Code Playgroud)

我们的timeit语句如下所示:

timeit.timeit(stmt=stmt1, setup=setup)
Run Code Online (Sandbox Code Playgroud)

timeit.timeit(stmt=stmt2, setup=setup)
Run Code Online (Sandbox Code Playgroud)

现在,在我们的两台笔记本电脑上运行它(相同的硬件,类似的处理负载)stmt1(带有if语句的语句)即使在多次运行后也会运行得更快(3-4个百分点,大约四分之一) stmt2的第二个).

但是,如果我们定义函数来做两件事(包括创建变量的设置),如下所示:

def foo():
    str_var = '<string><substring><more_string>'
    if "<substring>" in str_var:
        str_var = str_var.replace("<substring>", "<new_substring>")
Run Code Online (Sandbox Code Playgroud)

def foo2():
    str_var = '<string><substring><more_string>'
    str_var = str_var.replace("<substring>", "<new_substring>")
Run Code Online (Sandbox Code Playgroud)

和运行timeit像:

timeit.timeit("foo()", setup="from __main__ import foo")
timeit.timeit("foo2()", setup="from __main__ import foo2")
Run Code Online (Sandbox Code Playgroud)

没有if语句(foo2)的语句运行得更快,与非功能结果相矛盾.

我们是否遗漏了Timeit的工作原理?或者Python如何处理这样的案例?

编辑这里是我们的实际代码:

>>> def foo():
    s = "hi 1 2 3"
    s = s.replace('1','5')

>>> def foo2():
    s = "hi 1 2 3"
    if '1' in s:
        s = s.replace('1','5')


>>> timeit.timeit(foo, "from __main__ import foo")
0.4094226634183542
>>> timeit.timeit(foo2, "from __main__ import foo2")
0.4815539780738618
Run Code Online (Sandbox Code Playgroud)

vs这段代码:

>>> timeit.timeit("""s = s.replace("1","5")""", setup="s = 'hi 1 2 3'")
0.18738432400277816
>>> timeit.timeit("""if '1' in s: s = s.replace('1','5')""", setup="s = 'hi 1 2 3'")
0.02985000199987553
Run Code Online (Sandbox Code Playgroud)

jpm*_*c26 5

我想我已经明白了.

看看这段代码:

timeit.timeit("""if '1' in s: s = s.replace('1','5')""", setup="s = 'hi 1 2 3'")
Run Code Online (Sandbox Code Playgroud)

在此代码中,setup只运行一次.这意味着它s变成了"全球化".结果,它hi 5 2 3在第一次迭代中被修改为in现在False,并且现在返回所有连续迭代.

看到这段代码:

timeit.timeit("""if '1' in s: s = s.replace('1','5'); print(s)""", setup="s = 'hi 1 2 3'")
Run Code Online (Sandbox Code Playgroud)

这将打印出hi 5 2 3一次,因为它printif声明的一部分.对比这个,这将填满你的屏幕与吨吨hi 5 2 3:

timeit.timeit("""s = s.replace("1","5"); print(s)""", setup="s = 'hi 1 2 3'")
Run Code Online (Sandbox Code Playgroud)

所以这里的问题是带有if测试的非功能是有缺陷的并且给你错误的时间,除非对你已经处理的字符串的重复调用是你试图测试的.(如果它是你试图测试的,你的函数版本是有缺陷的.)函数的if优点不公平的原因是因为它replace在每次迭代时运行字符串的新副本.

下面的测试做什么,我相信你意,因为它不重新分配的结果replaces,留下未修改每次迭代:

>>> timeit.timeit("""if '1' in s: s.replace('1','5')""", setup="s = 'hi 1 2 3'"
0.3221409016812231
>>> timeit.timeit("""s.replace('1','5')""", setup="s = 'hi 1 2 3'")
0.28558505721252914
Run Code Online (Sandbox Code Playgroud)

这个改变为if测试增加了很多时间,if为我增加了一些非测试时间,但我使用的是Python 2.7.但是,如果Python 3的结果是一致的,那么这些结果表明,in当字符串很少需要替换时,可以节省大量时间.如果他们通常需要更换,则需要in花费一点时间.