在代码实现过程中,我得到建议我们应该只导入特定的功能,这将减少时间,但我得到了一些不同的结果。我做错了什么。
python -m timeit "from subprocess import call" (suggested method which is taking more time)
1000000 loops, best of 3: 1.01 usec per loop
python -m timeit "import subprocess"
1000000 loops, best of 3: 0.525 usec per loop
答案 0 :(得分:1)
首先,任何严重的测试都需要超过2次执行。但我认为你已经完成了更多相似的结果。
改进的不是import
的时间,而是一般的执行时间。特别是解释器解析任何类型符号所需的时间。
我建议您使用生产中最大的模块,并将所有常规导入替换为特定的模块和时间此。
更喜欢特定导入的另一个原因是文档:了解程序的功能以及程序是如何通过导入进行的。如果导入是特定的,则此信息也更具体。
答案 1 :(得分:1)
从模块导入特定函数的原因更多的是在函数中具有可管理的命名空间而不是速度。正如Joachim Pileborg在你的评论中所建议的那样,使用from ... import
带来的函数的实际执行速度更快,但即便如此,你也许不会注意到绝大多数脚本的差异。
最好的办法是导入模块,使代码最易于阅读和维护(例如,您希望在代码中看到call()
或subprocess.call()
吗?)并专注仅在代码的一小部分是瓶颈的特定情况下,或者您的代码总体上太慢时才会出现性能。
答案 2 :(得分:1)
由于您没有使用正在导入的模块的任何代码,因此这种分析无用。
包含实际使用子进程模块的代码。您可能会发现两种类型的进口之间的差异具有边际后果。
答案 3 :(得分:1)
import subprocess
和from subprocess import call
之间的性能差异可能非常小(可能它将完全由实际创建和运行其他进程所涉及的开销所主导)。但是,它们之间存在一些小的性能差异,可能值得看看它们来自何处。
这是我用来了解他们所做事情的代码。我使用dis
模块来反汇编编译的Python字节码,所以我们可以看到发生了什么:
import dis
from_import_src = """
from subprocess import call
foo = call
"""
plain_import_src = """
import subprocess
foo = subprocess.call
"""
from_bytecode = compile(from_import_src, "from_import", "exec")
plain_bytecode = compile(plain_import_src, "plain_import", "exec")
print("from ... import ...")
dis.dis(from_bytecode)
print("\nimport ...")
dis.dis(plain_bytecode)
输出:
from ... import ...
2 0 LOAD_CONST 0 (0)
3 LOAD_CONST 1 (('call',))
6 IMPORT_NAME 0 (subprocess)
9 IMPORT_FROM 1 (call)
12 STORE_NAME 1 (call)
15 POP_TOP
3 16 LOAD_NAME 1 (call)
19 STORE_NAME 2 (foo)
22 LOAD_CONST 2 (None)
25 RETURN_VALUE
import ...
2 0 LOAD_CONST 0 (0)
3 LOAD_CONST 1 (None)
6 IMPORT_NAME 0 (subprocess)
9 STORE_NAME 0 (subprocess)
3 12 LOAD_NAME 0 (subprocess)
15 LOAD_ATTR 1 (call)
18 STORE_NAME 2 (foo)
21 LOAD_CONST 1 (None)
24 RETURN_VALUE
如果您不熟悉dis.dis
输出,请在此处简要概述:第一列是源代码行(因为我们的来源'第一行是空白的,真实的代码在第2和第3行)。第三列显示要运行的字节码指令,最后一列显示它接收的参数。其他数字并不重要。
现在,并非每个Python字节码都需要相同的执行时间,但作为第一个近似值,比较两个代码中每一行的指令数可以表明它们在性能上的差异。
使用from ... import ...
的第一个版本在导入步骤中会做更多工作。也就是说,它首先导入模块,然后从中加载call
属性并将其保存到本地名称空间中。这个前期成本虽然在我们访问call
函数以将其保存到另一个变量的第二步中得到回报。由于call
已经位于顶级命名空间中,因此只需要一次查找。
使用import ...
的第二个版本在导入行上的前期工作较少,但必须执行两次查找才能解析第二行中的subprocess.call
。
因此,我们可以得出结论,如果您多次访问属性,from ... import ...
方法可能会更快。另一方面,如果您只访问该属性一次,则使用普通import ...
可能会降低开销。
但实际上,进口产品的表现可能不是很重要。使用任何使代码最具可读性的内容,只有在分析告诉您存在重大问题时才会担心性能。