Python编译器是否识别函数中定义的常量,这样它只会计算一次它们的值,而不管函数随后在代码中调用多少次?
例如,
def f():
x = [ 1, 2, 3, 4 ]
# stuff
for i in range( 100 ):
f()
将x
重新计算100次f()
?
并不总是可以在使用它们的函数之外定义常量,并且我很好奇Python是否已经在这些情况下退缩了。
答案 0 :(得分:3)
(请注意,这适用于CPython,在其他实现中可能有所不同)
解析Python代码并将其编译为字节码。您可以看到dis
模块使用的说明。
>>> def f(x):
... x = [1, 2, 3, 4]
>>> dis.dis(f)
2 0 LOAD_CONST 1 (1)
2 LOAD_CONST 2 (2)
4 LOAD_CONST 3 (3)
6 LOAD_CONST 4 (4)
8 BUILD_LIST 4
10 STORE_FAST 0 (x)
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
>>> print(dis.Bytecode(f).info())
Name: f
Filename: <stdin>
Argument count: 1
Kw-only arguments: 0
Number of locals: 1
Stack size: 4
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
1: 1
2: 2
3: 3
4: 4
Variable names:
0: x
如您所见,整数文字是常量,但每次都必须构建列表。
这是一个相对较快的操作(可能比查找全局更快,但时间仍然可以忽略不计)
如果你有一个使用元组的函数g
,它将作为常量加载:
>>> def g(x):
... x = (1, 2, 3, 4)
>>> dis.dis(g)
2 0 LOAD_CONST 5 ((1, 2, 3, 4))
2 STORE_FAST 0 (x)
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
>>> print(dis.Bytecode(g).info())
Name: g
Filename: <stdin>
Argument count: 1
Kw-only arguments: 0
Number of locals: 1
Stack size: 4
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
1: 1
2: 2
3: 3
4: 4
5: (1, 2, 3, 4)
Variable names:
0: x
但这似乎是一个过早优化的案例。
为函数存储的常量可以找到function.__code__.co_consts
。
>>> g.__code__.co_consts
(None, 1, 2, 3, 4, (1, 2, 3, 4))
每次必须构建新列表的原因是,如果更改列表,它将不会影响每次加载的列表。
如果它不是常量列表,那么元组优化会消失。
>>> def h(x):
... x = (1, 2, 3, x)
>>> dis.dis(h)
2 0 LOAD_CONST 1 (1)
2 LOAD_CONST 2 (2)
4 LOAD_CONST 3 (3)
6 LOAD_FAST 0 (x)
8 BUILD_TUPLE 4
10 STORE_FAST 0 (x)
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
>>> print(dis.Bytecode(h).info())
Name: h
Filename: <stdin>
Argument count: 1
Kw-only arguments: 0
Number of locals: 1
Stack size: 4
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
1: 1
2: 2
3: 3
Variable names:
0: x
答案 1 :(得分:1)
简短回答:对于列表,不。
如果我们在使用dis
进行编译后检查中间代码,我们会看到:
>>> dis.dis(f)
2 0 LOAD_CONST 1 (1)
3 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
9 LOAD_CONST 4 (4)
12 BUILD_LIST 4
15 STORE_FAST 0 (x)
18 LOAD_CONST 0 (None)
21 RETURN_VALUE
因为你可以看到程序首先将常量1
加载到4
并将它们推送到堆栈上,并构造一个包含这些常量的列表,这意味着它构造了一个列表每次。
如果列表没有变异,我建议定义函数外的常量:
some_constant = [1, 2, 3, 4]
def f():
# use some_constant
# ...
pass