为什么python没有优化循环中的函数声明?

时间:2017-08-17 07:42:52

标签: python function optimization

考虑一下:

Code A

def foo(): 
    pass

for i in range(1000000):
    foo()

Code B

for i in range(1000000):
    def foo():
        pass
    foo()

两个代码片段之间的唯一区别是foo在每次迭代时不断在循环内重新定义。

运行一些基准测试:

Code A

10 loops, best of 3: 102 ms per loop

Code B

10 loops, best of 3: 188 ms per loop

因此,不断重新定义函数是一种不必要的开销。

以下是Code B的字节代码:

  1           0 SETUP_LOOP              39 (to 42)
              3 LOAD_NAME                0 (range)
              6 LOAD_CONST               0 (1000000)
              9 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             12 GET_ITER
        >>   13 FOR_ITER                25 (to 41)
             16 STORE_NAME               1 (i)

  2          19 LOAD_CONST               1 (<code object foo at 0x103113390, file "<dis>", line 2>)
             22 LOAD_CONST               2 ('foo')
             25 MAKE_FUNCTION            0
             28 STORE_NAME               2 (foo)

  4          31 LOAD_NAME                2 (foo)
             34 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
             37 POP_TOP
             38 JUMP_ABSOLUTE           13
        >>   41 POP_BLOCK
        >>   42 LOAD_CONST               3 (None)
             45 RETURN_VALUE

如您所见,函数定义尚未在循环之外进行优化(参见第25 MAKE_FUNCTION行)。

这似乎很简单,将函数创建移出循环,因为它的声明显然不是循环执行的条件。

是否有任何明显的障碍阻止这种情况发生?

4 个答案:

答案 0 :(得分:2)

Python允许在运行时重新分配或修改很多东西。例如,在编译此代码时,Python无法确定程序的某些部分是否可能执行类似

的操作
import builtins

builtins.range = lambda *args: []

在这种情况下将foo定义移出循环是错误的,因为foo定义永远不会执行。

你可以在Python中做很多很多疯狂的事情,可以用意想不到的方式改变代码的含义。尽管有这样的可能性进行优化确实需要JIT编译器,但Python的标准实现并不具备其中之一。

答案 1 :(得分:1)

以下是您建议的优化不起作用的示例

foo = None

def range(n):
    global foo
    def foo():
        print('hi')
    for i in (1,2,3):
        yield i

def foo():
    pass

for i in range(1000000):
    foo()

我的输出:

hi
hi
hi

答案 2 :(得分:0)

如果要在运行时更改函数定义,该怎么办?

for i in range(1000000):
    if(True):
        def foo():
            pass
    if("some bits decide to flip"=="some bits decide to flip"):
        def foo():
            i=i+1
            pass
    foo()

答案 3 :(得分:0)

由于python不知道你是否在范围内捕获某些内容,因此无法对其进行优化。如果你在循环中声明函数,那么你需要重新编写或不重新编译。 当python到达循环时,解释器不能超出循环范围。