2017-08-17 76 views
-2

考虑:为什么python不能优化循环中的函数声明?

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)。

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

是否有任何明显的障碍妨碍了这一点的完成?

+1

Python做很少的优化,在一般。 – user2357112

+5

(您可以轻松地问为什么它不会优化整个循环。) – user2357112

+0

您是否试过[Pypy](https://pypy.org/),这是一个即时编译器? – gaborous

回答

2

Python允许在运行时重新分配或修改很多东西。例如,编译此代码时,Python不能确定你的程序的某一部分是否会做这样的事情

import builtins 

builtins.range = lambda *args: [] 

在这种情况下,移动foo定义圈外的将是错误的,因为foo定义应从不执行。

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

+0

谢谢。这是有道理的。在回答“为什么不这样做”的问题时,首先谈论“为什么不应该这样做”是有道理的。 –

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() 
0

由于python不知道你是否在范围内捕获某些东西,所以他无法优化它。如果你在循环中声明了函数,那么你需要它来重新定义它。 当python到达循环的时候,解释器不能离开循环范围。

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