Python - 及时克服导入

Posted

技术标签:

【中文标题】Python - 及时克服导入【英文标题】:Python - Overcome import in timeit 【发布时间】:2018-06-01 05:34:44 【问题描述】:

我最近注意到timeit python 模块的以下内容:

在我的机器上:

from timeit import Timer
t = Timer(stmt='a = 2**3**4')
print("This took :.3fs to execute.".format(t.timeit()))

将产生:

这需要 0.017 秒来执行。

另一方面写一个文件test.py

#!/usr/bin/env python3

a = 2**3**4

并调用:

from timeit import Timer
t = Timer(stmt='import test')
print("This took :.3fs to execute.".format(t.timeit()))

将产生:

这需要 0.126 秒来执行。

我想知道如何在不更改文件本身的情况下测试test.py 的执行时间。我如何才能解决导入文件的问题(因此会浪费时间)。

【问题讨论】:

***.com/a/24105845/650884 如果是第一次调用import test,python 将忙于创建“test.pyc”并需要更长的时间。后续导入应该会快一些。 @Aaron:除了后续的导入根本不会导入任何东西(他们经历了一堆导入复杂的过程,最终从sys.modules拉出缓存的模块,这不会重新运行模块完全)。 【参考方案1】:

您将获得的最接近的方法是将compileexec 一起使用。如果您打算以 .pyc 文件的形式运行,请不要在计时的内容中包含编译语句。

# time as if executing:" >python test.pyc " from terminal
#   (importing test.py will typically automatically generate the .pyc file automatically)
t = Timer(stmt='exec(code_object)', 
          setup='code_object = compile(open("test.py").read(), "test.py", "exec")')

# time as if executing:" >python test.py " from terminal
t = Timer(stmt='exec(compile(open("test.py").read(), "test.py", "exec"))')

这应该让您接近从终端调用脚本的实际时间。这并不能消除开销,因为调用脚本的开销是真实存在的,并且会在现实世界中观察到。

如果您使用的是基于 linux 的系统,您也可以从终端调用 >time test.py

【讨论】:

@StefanPochmann 对不起,愚蠢的错别字。不去想我在做什么。见编辑 好的,现在它可以工作了,但是......大约需要 0.35 秒,而 OP 的方式只需要 0.15 秒。当您测试它时,它真的对您更快吗?这次你做了测试,对吧? :-P 我想给一个脚本计时,只要我把它全部放在引号中并把它作为stmt 放在timeit 中。 >time test.py 总是打印更多时间,因为包含了 python 启动。我将如何解决这个问题?我想测试一些代码...code...,就好像我使用了print(Timer.timeit(stmt='code')) 也许你也想看看我的next question @StefanPochmann 这不是更快,它是真实世界的代表【参考方案2】:

当你写作时,你的测量存在一个问题,你没有测量你认为你正在测量的东西:

t = Timer(stmt= 'a = 2**3**4')

您正在测量绑定时间!看:

>>> Timer(stmt='a = 10**5**4').timeit(100000)
    0.0064544077574026915
>>> Timer(stmt='a = 2**3**4').timeit(100000)
    0.006511381058487586

时间几乎相同,但计算 10**5**42**3**4 要长一些。 2**3**4 只计算一次,在编译代码时,这称为“常量折叠”,Python 在编译源代码期间执行的一些优化。

比较这两个结果:

>>> Timer(stmt= 'a = 2**3**4').timeit(100000) 
    0.00628656749199763
>>> Timer(stmt= 'a = x**y**z', setup='(x,y,z)=(2,3,4)').timeit(100000) 
    0.18055968312580717

但这种加速并不是免费的。有两点:

    编译时间增加 .pyc 文件大小增加(因为此值存储在 .pyc 文件中)

假设我有两个文件:

#foo1.py
a = 10**7**7

#foo2.py
x,y,z =(10,7,7)
a = x**y**z

如果我用python -m py_compile foo1.py foo2.py 编译它们,我机器上.pyc 文件的大小将为:

    foo1.cpython-36.pyc - 364 882 字节 foo2.cpython-36.pyc - 150 字节

【讨论】:

这真的很有趣,但我不太明白这与我的导入语句有什么关系?我在想,当我导入test.py 时,已经存在一个包含常量的test.pyc,因此代码应该比动态生成它更快(例如在命令行中),而实际上使用导入更慢. 也许你也想看看我的next question

以上是关于Python - 及时克服导入的主要内容,如果未能解决你的问题,请参考以下文章

如何克服 python 多处理中的开销?

克服 Python 字典的时间复杂度

克服 Python 2.3 中的 os.system() 限制

如何克服 Python 入门难以进步的现象?

使用 qBittorrent 运行脚本时如何克服 Python 中的 IO 错误 13

无法使用pip安装openpyxl - 如何克服代理