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 ...
可能具有较低的开销。
但实际上,进口的表现可能不是一件非常重要的事情。使用任何使您的代码具有最高可读性的功能,并且只有在性能分析告诉您存在重大问题时才担心性能。
你应该只导入你需要的特定功能的原因之一就是你不会打破命名空间。 –
而功能特定导入速度较慢的原因是因为解释器首先必须导入完整的模块,以便能够找到所需的功能。如果您使用功能特定的导入与全部模块导入相比,您可能还想测试*使用*该功能是否有所不同。 –
从技术上讲,推理基本上是不存在的,而且在风格上,除非你只使用模块中的单个对象,'import module'比模块导入的东西,小工具,装饰物更加明确和清晰。 –