Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception during list comprehension. Are intermediate results kept anywhere?

When using try-except in a for loop context, the commands executed so far are obviously done with

a = [1, 2, 3, 'text', 5]

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

results with

Error!
[5, 6, 7]

However the same is not true for list comprehensions

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

And the result is

Error!
[]

Is the intermediate list, built before the exception occurred, kept anywhere? Is it accessible?

like image 561
Aguy Avatar asked Jul 07 '16 06:07

Aguy


2 Answers

The list comprehension intermediate results are kept on an internal CPython stack, and are not accessible from the Python expressions that are part of the list comprehension.

Note that Python executes the [.....] first, which produces a list object, and only then assigns that result to the name c. If an exception occurs within the [....] expression, the expression is terminated and exception handling kicks in instead. Your print(c) expression thus can only ever show the previous object that c was bound to, which here is an empty list object. It could have been anything else:

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

In your first example, no new list object is produced. You instead manipulate (using b.append()) an existing list object, which is why you can see what all successful b.append() calls have done to it.

like image 96
Martijn Pieters Avatar answered Oct 18 '22 08:10

Martijn Pieters


Let's look at the bytecode:

>>> 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...

As you can see, the list comprehension is translated to a series of instructions GET_ITER...JUMP_ABSOLUTE. The next instruction STORE_FAST is the one that modifies c. If any exception occurs before it, c will not have been modified.

like image 38
sinelaw Avatar answered Oct 18 '22 06:10

sinelaw