考虑一下:
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
行)。
这似乎很简单,将函数创建移出循环,因为它的声明显然不是循环执行的条件。
是否有任何明显的障碍阻止这种情况发生?
答案 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到达循环时,解释器不能超出循环范围。