为何locals()返回奇怪的自引用列表?
我在函数里用locals()来获取一些参数,这样做效果很好:
def my_function(a, b):
print locals().values()
>>> my_function(1,2)
[1, 2]
这都是一些常见的操作。不过现在我们来引入一个列表推导式:
def my_function(a, b):
print [x for x in locals().values()]
>>> my_function(1,2)
[[...], 1, 2]
咦?为什么会出现自我引用的情况呢?
1 个回答
25
在Python 2.7和3.1之前的版本中,生成列表推导式时使用的字节码并不是最优的。在这些版本中,列表推导式会被存储在一个局部变量中(如果在模块范围内,甚至会存储在全局变量中):
>>> import dis
>>> def foo():
... return [x for x in y]
...
>>> dis.dis(foo)
2 0 BUILD_LIST 0
3 DUP_TOP
4 STORE_FAST 0 (_[1])
7 LOAD_GLOBAL 0 (y)
10 GET_ITER
>> 11 FOR_ITER 13 (to 27)
14 STORE_FAST 1 (x)
17 LOAD_FAST 0 (_[1])
20 LOAD_FAST 1 (x)
23 LIST_APPEND
24 JUMP_ABSOLUTE 11
>> 27 DELETE_FAST 0 (_[1])
30 RETURN_VALUE
这里的_[1]
局部变量就是正在构建的列表。当嵌套使用列表推导式时,会用递增的整数来表示结果:
>>> def bar():
... return [[x for x in y] for z in spam]
...
>>> dis.dis(bar)
2 0 BUILD_LIST 0
3 DUP_TOP
4 STORE_FAST 0 (_[1])
7 LOAD_GLOBAL 0 (spam)
10 GET_ITER
>> 11 FOR_ITER 40 (to 54)
14 STORE_FAST 1 (z)
17 LOAD_FAST 0 (_[1])
20 BUILD_LIST 0
23 DUP_TOP
24 STORE_FAST 2 (_[2])
27 LOAD_GLOBAL 1 (y)
30 GET_ITER
>> 31 FOR_ITER 13 (to 47)
34 STORE_FAST 3 (x)
37 LOAD_FAST 2 (_[2])
40 LOAD_FAST 3 (x)
43 LIST_APPEND
44 JUMP_ABSOLUTE 31
>> 47 DELETE_FAST 2 (_[2])
50 LIST_APPEND
51 JUMP_ABSOLUTE 11
>> 54 DELETE_FAST 0 (_[1])
57 RETURN_VALUE
通过循环访问locals().values()
,你会在返回值中包含对正在构建的列表的引用。需要注意的是,字节码使用了DELETE_FAST
来清理局部名称,以尽量避免命名空间的污染。
这个问题在Python 3.1和2.7中得到了优化,具体可以查看问题2183。正在构建的列表结果被移到了栈上。这个优化改变了LIST_APPEND
字节码,使其引用栈上哪个列表进行追加,从而不再需要在开始时使用DUP_TOP
-> STORE_FAST
,每次迭代时使用LOAD_FAST
,以及在列表推导式后使用DELETE_FAST
。