列表理解期间的例外情况。中间结果是否保留在何处?

时间:2016-07-07 06:59:15

标签: python list-comprehension

在for循环上下文中使用try-except时,到目前为止执行的命令显然是用

完成的
a = [1, 2, 3, 'text', 5]

b = []
try:
    for k in range(len(a)):
        b.append(a[k] + 4)
except:
    print('Error!')
print(b)

的结果

Error!
[5, 6, 7]

然而,列表推导不一样

c=[]
try:
    c = [a[k] + 4 for k in range(len(a))]
except:
    print('Error!')
print(c)

结果是

Error!
[]

在发生异常之前构建的中间列表是否保留在任何位置?它可以访问吗?

2 个答案:

答案 0 :(得分:26)

列表推导中间结果保存在内部CPython堆栈中,并且无法从列表推导中的Python表达式访问。

请注意,Python执行[.....] 第一个,它会生成一个列表对象,只有然后会将该结果分配给名称c。如果[....]表达式中发生异常,则表达式将终止,异常处理将启动。因此,您的print(c)表达式只能显示c绑定到的上一个对象,这里是一个空列表对象。它可能是其他任何东西:

>>> c = 'Anything else'
>>> try:
...     c = [2 // i for i in (1, 0)]
... except ZeroDivisionError:
...     pass
...
>>> c
'Anything else'

在第一个示例中,不会生成新的列表对象。您反而操纵(使用b.append()现有的列表对象,这就是为什么您可以看到所有成功的b.append()调用已对其执行的操作。

答案 1 :(得分:11)

让我们看看字节码:

>>> def example():
...     c=[]
...     try:
...         c = [a[k] + 4 for k in range(len(a))]
...     except:
...         print('Error!')
...     print(c)
... 
>>> import dis
>>> dis.dis(example)

--- removed some instructions       

             27 GET_ITER            
        >>   28 FOR_ITER                20 (to 51)
             31 STORE_FAST               1 (k)
             34 LOAD_GLOBAL              2 (a)
             37 LOAD_FAST                1 (k)
             40 BINARY_SUBSCR       
             41 LOAD_CONST               1 (4)
             44 BINARY_ADD          
             45 LIST_APPEND              2
             48 JUMP_ABSOLUTE           28
        >>   51 STORE_FAST               0 (c)

 --- more instructions...

如您所见,列表推导被转换为一系列指令GET_ITER ... JUMP_ABSOLUTE。下一条指令STORE_FAST是修改c的指令。如果在它之前发生任何异常,则c将不会被修改。