2013-08-27 17 views
0

在代码实现过程中,我得到了建议,我们应该只导入特定的功能,这将减少时间,但我得到了一些不同的结果。难道我做错了什么。导入模块或Python中的任何组件

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 
+2

你应该只导入你需要的特定功能的原因之一就是你不会打破命名空间。 –

+0

而功能特定导入速度较慢的原因是因为解释器首先必须导入完整的模块,以便能够找到所需的功能。如果您使用功能特定的导入与全部模块导入相比,您可能还想测试*使用*该功能是否有所不同。 –

+4

从技术上讲,推理基本上是不存在的,而且在风格上,除非你只使用模块中的单个对象,'import module'比模块导入的东西,小工具,装饰物更加明确和清晰。 –

回答

1

import subprocessfrom 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 ...可能具有较低的开销。

但实际上,进口的表现可能不是一件非常重要的事情。使用任何使您的代码具有最高可读性的功能,并且只有在性能分析告诉您存在重大问题时才担心性能。

+0

谢谢很多详细信息 – user765443

1

首先,任何严重的测试需要超过2次执行。但是我认为你已经完成了更多类似的结果。

改进的不是import的时间,而是一般的执行时间。具体来说,解释器需要花费时间来解析任何类型的符号。

我建议你采取生产中最大的模块,并用特定的时间和时间替换所有一般的进口这个

更喜欢特定导入的另一个原因是文档:了解程序的功能以及它如何通过导入操作。如果进口是特定的,这些信息也更具体。

1

从模块导入特定函数的原因更多的是在函数中具有可管理的名称空间,而速度较慢。正如Joachim Pileborg在您的评论中所建议的那样,使用from ... import引入的功能的实际执行速度可能会更快,但即使如此,您也可能没有注意到绝大多数脚本中的差异。

要做的最好的事情是导入模块的方式,使代码最易于读取和维护(例如,您希望在代码中看到call()还是subprocess.call()?),并只关注那些特定实例中的性能你的代码的一小部分是瓶颈,或者你的代码总体来说太慢了。

1

由于您没有任何使用正在导入的模块的代码,因此这种配置文件无用。

包含实际使用子流程模块的代码。您可能会发现两种进口产品之间的差异具有边际效应。