为什么locals()会返回一个奇怪的自引用列表?

时间:2014-03-10 17:47:44

标签: python list-comprehension python-2.6 python-internals 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]

EHH?为什么要插入自引用?

1 个答案:

答案 0 :(得分:25)

2.7和3.1之前的Python版本使用了次优的字节码来产生列表理解。在那些Python版本中,列表推导存储在局部变量中(如果在模块范围内,甚至是全局变量):

>>> 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进行了优化的,请参阅issue 2183。正在构建的列表结果被移动到堆栈中。优化更改了LIST_APPEND字节码,以引用要附加到堆栈上的列表,从而无需使用DUP_TOP - >开始时STORE_FAST,每次迭代LOAD_FAST,列表理解后DELETE_FAST