How to Handle Exceptions in a List Comprehensions

How to handle exceptions in a list comprehensions?

There is no built-in expression in Python that lets you ignore an exception (or return alternate values &c in case of exceptions), so it's impossible, literally speaking, to "handle exceptions in a list comprehension" because a list comprehension is an expression containing other expression, nothing more (i.e., no statements, and only statements can catch/ignore/handle exceptions).

Function calls are expression, and the function bodies can include all the statements you want, so delegating the evaluation of the exception-prone sub-expression to a function, as you've noticed, is one feasible workaround (others, when feasible, are checks on values that might provoke exceptions, as also suggested in other answers).

The correct responses to the question "how to handle exceptions in a list comprehension" are all expressing part of all of this truth: 1) literally, i.e. lexically IN the comprehension itself, you can't; 2) practically, you delegate the job to a function or check for error prone values when that's feasible. Your repeated claim that this is not an answer is thus unfounded.

Python exception handling in list comprehension

try:
[plot_pdf(f) for f in file_list] # using list comprehensions
except:
print ("Exception: ", sys.exc_info()[0])
continue

If plot_pdf(f) throws an error during execution of comprehension, then, it is caught in the except clause, other items in comprehension won't be evaluated.

It is not possible to handle exceptions in a list comprehension, for a list comprehension is an expression containing other expression, nothing more (i.e. no statements, and only statements can catch/ignore/handle exceptions).

Function calls are expression, and the function bodies can include all
the statements you want, so delegating the evaluation of the
exception-prone sub-expression to a function, as you've noticed, is
one feasible workaround (others, when feasible, are checks on values
that might provoke exceptions, as also suggested in other answers).

More here.

Raising an exception in a list comprehension: invalid syntax

Syntax

From a syntaxic point of view, your answer is in the full grammar spec.

The raise terminal only appears in rules derived from stmt (statement):

stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (... | flow_stmt | ...)
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
raise_stmt: 'raise' [test ['from' test]]

Whereas the first part of a list comprehension is a test (boolean or expression) or a star_expr (*expr):

atom: ... | '[' [testlist_comp] ']' | ...
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )

The point is there is no way a statement can be derived from the (test|star_expr) (left part of a list comprehension). Hence, your expression is syntactically wrong.

Semantic

As pointed by @Neb in a comment, a list comprehension trying to return a raise does not make sense.

You probably remember that print was a statement in Python 2 and became a function in Python 3:

Python 2:

>>> [print(1) for _ in range(1)]
File "<stdin>", line 1
[print(1) for _ in range(1)]
^
SyntaxError: invalid syntax

Python 3:

>>> [print(1) for _ in range(1)]
1
[None]

The list comprehension is now syntactically correct. Similarly, no syntaxic rule prevents you from writing this:

>>> def raiser(): raise ValueError('URI must be URL-encoded, ASCII only!')
...
>>> def redirect(uri): [raiser() for c in uri if not (32 <= ord(c) <= 127)]
...
>>> redirect("abc")
>>> redirect("éàç")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in redirect
File "<stdin>", line 1, in <listcomp>
File "<stdin>", line 1, in raiser
ValueError: URI must be URL-encoded, ASCII only!

But the semantic remains unclear: do you want to perform an action (ie use a side effect from a function), or to build a list? Remember that list comprehensions are a borrowing to functional langages, expecially Haskell I think. Hence, they are not here to perform actions.

I quote @Mark Ransom comment to an answer to the "Is it Pythonic to use list comprehensions for just side effects?" question:

I would go even further and state that side effects inside a list comprehension are unusual, unexpected, and therefore evil, even if you're using the resulting list when you're done. – Mark Ransom

I use this rule of thumb: avoid any side effects in list comprehensions, even if you use the result.

Error handling for list comprehension in Python

you cannot catch an exception from a list comprehension (How can I handle exceptions in a list comprehension in Python?). But given what you want to do you could use get:

values = [i.get("row") for i in [{"row" : 1}, {"Row" : 0}, {}]]

if the key isn't found in the dictionary get returns None, exactly what you're looking for (it can return anything you want, just by passing the default value as second argument, see Why dict.get(key) instead of dict[key]?)

Can you embed a try in a Python list comprehension?

No, you can't. You can put only for loops, if's and else's.

What you can do, though, is use .get(), which never throws a KeyError:

my_list = [my_dict.get("some_key", 0) for my_dict in my_list]

The second argument is the default value in case the key does not exist. If you don't specify a default, the default default is None.

Python list comprehension: Skip elements that raise exception

I think your first solution is clear, and that's the most important thing in python. Alternatively, how about a generator instead of a comprehension?

def try_div_itr(itr):
for elem in itr:
try:
yield 1 / elem
except ZeroDivisionError:
pass

result = list(try_div_itr([-2, -1, 0, 1, 2]))

You could even generalise this

def try_itr(func, itr, *exceptions, **kwargs):
for elem in itr:
try:
yield func(elem, **kwargs)
except exceptions:
pass

x = [random.choice([0, 1]) for _ in range(100_000)]

%timeit [i for i in (try_div(i) for i in x) if i is not None]
42.6 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit [i for i in [try_div(i) for i in x] if i is not None]
36.3 ms ± 154 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit list(try_div_itr(x))
25.3 ms ± 85.1 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit list(try_itr(lambda i: 1/i, x, ZeroDivisionError))
34.7 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# but expect generic method to be slower anyway

%timeit list(try_itr((1).__truediv__, x, ZeroDivisionError))
28.7 ms ± 118 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# remove lambda abstraction for speed

Using list comprehensions and exceptions?

lets say i want to the raise an exception when a required value is missing, with the value that it is missing. How can i do that using list comprehension?

List comprehensions are a syntactically concise way to create a list based on some existing list—they're not a general-purpose way of writing any for-loop in a single line. In this example, you're not actually creating a list, so it doesn't make any sense to use a list comprehension.

How to handle exceptions in dictionary comprehension

You can test for 0 with an inline if:

lengths[k] / v if v != 0 else 0 directly in the comprehension



Related Topics



Leave a reply



Submit