0

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

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
4

4 回答 4

1

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

最好的办法是以使代码对您来说最易读和可维护的方式导入模块(例如,您更愿意在代码中看到call()还是subprocess.call()在代码中?)并且只关注那些特定情况下的性能代码是一个瓶颈,或者你的代码总体上太慢了。

于 2013-08-27T06:21:38.840 回答
1

这种分析没有用,因为您没有任何使用正在导入的模块的代码。

包含实际使用 subprocess 模块的代码。您可能会发现两种类型的进口之间的差异具有边际影响。

于 2013-08-27T06:23:19.580 回答
1

首先,任何严肃的测试都需要执行 2 次以上。但我假设你已经完成了更多类似的结果。

改进的不是import时间,而是一般的执行时间。特别是解释器解析任何类型的符号所花费的时间。

我建议您使用您在生产中拥有的最大模块,并将所有通用导入替换为特定的导入并计时this

偏爱特定导入的另一个原因是文档:了解程序的功能以及通过导入来实现它的方式。如果导入是特定的,则此信息也更具体。

于 2013-08-27T06:18:18.990 回答
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 ...可能会降低开销。

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

于 2013-08-27T07:39:31.907 回答