我使用python -mtimeit
进行测试,发现from Module import Sth
与import Module
相比需要更多时间
E.g。
$ python -mtimeit "import math; math.sqrt(4)"
1000000 loops, best of 3: 0.618 usec per loop
$ python -mtimeit "from math import sqrt; sqrt(4)"
1000000 loops, best of 3: 1.11 usec per loop
与其他情况相同。有人可以解释背后的理由吗?谢谢!
答案 0 :(得分:2)
这里有两个问题。第一步是确定哪个部分更快:import语句或调用。
所以,让我们这样做:
$ python -mtimeit 'import math'
1000000 loops, best of 3: 0.555 usec per loop
$ python -mtimeit 'from math import sqrt'
1000000 loops, best of 3: 1.22 usec per loop
$ python -mtimeit -s 'from math import sqrt' 'sqrt(10)'
10000000 loops, best of 3: 0.0879 usec per loop
$ python -mtimeit -s 'import math' 'math.sqrt(10)'
10000000 loops, best of 3: 0.122 usec per loop
(那就是我的笔记本电脑上的OS X 10.6.4上的Apple CPython 2.7.2 64位。但是同一台笔记本电脑上的python.org 3.4开发和Linux机器上的3.3.1开发结果大致相同。使用PyPy,更智能的缓存使得无法测试,因为所有内容都在1ns完成......无论如何,我认为这些结果可能与microbenchmarks一样可移植。)
事实证明,import
语句的速度是其两倍多;之后,调用函数有点慢,但还不足以弥补更便宜的import
。 (请记住,您的测试每次调用都会import
。在现实生活中的代码中,您往往会按import
调用一次以上的内容。所以,我们是真正关注一个很少会影响实际代码的边缘情况。但只要你记住这一点,我们就会继续。)
从概念上讲,您可以理解为什么from … import
语句需要更长时间:它还有更多工作要做。第一个版本必须找到模块,必要时编译它并执行它。第二个版本必须完成所有这些,然后也提取sqrt
并将其插入到当前模块的全局变量中。所以,它必须至少有点慢。
如果你查看字节码(例如,使用dis
模块并调用dis.dis('import math')
),这正是区别。比较:
0 LOAD_CONST 0 (0)
3 LOAD_CONST 1 (None)
6 IMPORT_NAME 0 (math)
9 STORE_NAME 0 (math)
12 LOAD_CONST 1 (None)
15 RETURN_VALUE
......来:
0 LOAD_CONST 0 (0)
3 LOAD_CONST 1 (('sqrt',))
6 IMPORT_NAME 0 (math)
9 IMPORT_FROM 1 (sqrt)
12 STORE_NAME 1 (sqrt)
15 POP_TOP
16 LOAD_CONST 2 (None)
19 RETURN_VALUE
额外的堆栈操作(LOAD_CONST
和POP_TOP
)可能没什么区别,使用STORE_NAME
的不同参数根本不重要......但是{ {1}}是一个重要的额外步骤。
令人惊讶的是,对IMPORT_FROM
代码进行简要分析的快速和肮脏尝试表明,大部分成本实际上都在查找要导入的相应全局变量。我不确定为什么,但是......这意味着导入一大堆名称应该不比仅导入一个名称慢得多。并且,正如您在评论中指出的那样,这正是您所看到的。 (但是请不要过多地阅读。IMPORT_FROM
可能有很大的常数因子,只有一个很小的线性因素,而且我们并没有完全抛出大量的名字。
最后一件事:如果这真的在真实代码中确实很重要,并且你想要充分利用这两个世界,那么IMPORT_FROM
比import math; sqrt = math.sqrt
更快,但是给你同样的小加速查询/通话时间。 (但同样,我无法想象任何真实的代码,这将是重要的。你唯一需要关心from math import sqrt
需要多长时间,当你称它为十亿次,此时你赢了'请注意导入需要多长时间。另外,如果您确实需要对其进行优化,请创建一个本地范围并绑定sqrt
以完全避免全局查找。)
答案 1 :(得分:0)
这不是答案,而是一些信息。它需要格式化,所以我没有把它作为评论。这是'from math import sqrt'的字节码:
>>> from math import sqrt
>>> import dis
>>> def f(n): return sqrt(n)
...
>>> dis.dis(f)
1 0 LOAD_GLOBAL 0 (sqrt)
3 LOAD_FAST 0 (n)
6 CALL_FUNCTION 1
9 RETURN_VALUE
对于'导入数学'
>>> import math
>>> import dis
>>> dis.dis(math.sqrt)
>>> def f(n): return math.sqrt(n)
...
>>> dis.dis(f)
1 0 LOAD_GLOBAL 0 (math)
3 LOAD_ATTR 1 (sqrt)
6 LOAD_FAST 0 (n)
9 CALL_FUNCTION 1
12 RETURN_VALUE
有趣的是,更快的方法还有一条指令。