One-Line Exception Handling

Question:

In Python, it is possible to use one-liners to set values with special conditions (such as defaults or conditions) in a simple, intuitive way.

result = 0 or "Does not exist."  # "Does not exist."

result = "Found user!" if user in user_list else "User not found."

Is it possible to write a similar statement that catches exceptions?

from json import loads

result = loads('{"value": true}') or "Oh no, explosions occurred!"
# {'value': True}

result = loads(None) or "Oh no, explosions occurred!"
# "Oh no, explosions occurred!" is desired, but a TypeError is raised.
Asked By: 2Cubed

||

Answers:

It is not possible to do a one-line exception-handling statement in python. One could write a function to do this.

def safe_execute(default, exception, function, *args):
    try:
        return function(*args)
    except exception:
        return default

Example usage:

from json import loads
safe_execute("Oh no, explosions occurred!", TypeError, loads, None)
# Returns "Oh no, explosions occurred!"
safe_execute("Huh?", TypeError, int, "10")
#Returns 10

Multiple arguments are supported

from operator import div
safe_execute(
    "Divsion by zero is invalid.",
    ZeroDivisionError,
    div, 1, 0
)
# Returns "Divsion by zero is invalid."

safe_execute(
    "Divsion by zero is invalid.",
    ZeroDivisionError,
    div, 1, 1
)
# Returns 1.

The error-catching process may still be interrupted:

from time import sleep
safe_execute(
    "Panic!",
    Exception,
    sleep, 8
)
# Ctrl-c will raise a KeyboardInterrupt

from sys import exit
safe_execute("Failed to exit!", Exception, exit)
# Exits the Python interpreter

If this behavior is undesired, use BaseException:

from time import sleep
safe_execute("interrupted",
             BaseException,
             sleep, 8)
#Pressing Ctrl-c will return "interrupted"
from sys import exit
safe_execute("Naughty little program!",
             BaseException,
             exit)
#Returns "Naughty little program!"
Answered By: pppery

It is possible in one line using exec:

parse_float = lambda x, y=exec("def f(s):n try:n  return float(s)n except:  return None"): f(x)
Answered By: user6035995

You can use contextlib to suppress exceptions. If you like to live dangerously you could suppress BaseException, which would suppress all builtin exceptions (probably a bad idea). Or you could pick a safe one relevant to your code, like TypeError.

examples:

from contextlib import suppress

# this will execute right along
with suppress(BaseException): fhasldjkfhsa345315

# even raising an Exception will fly just fine
with suppress(BaseException): raise NameError

# correct code will execute just fine
x=5
with suppress(BaseException): x+=2
print(x) # prints 7

# and in your example:
from json import loads
pleasure = suppress(TypeError) # so each line rolls off the tongue :)

with pleasure: result = loads('{"value": True}')
print(result) # prints {'value': True}

with pleasure: result = loads(None)
print(result) # prints {'value': True} because result never changed
Answered By: Fnord

pppery’s answer is a more complete solution, but I prefer a more terse (and simpler) approach:

def maybe(function):
    try:
        return function()
    except:
        return None

With this utility, we can do stuff like this:

# Instead of doing this
last_item = self.stock.items[-1] if self.stock and self.stock.items else None

# It can be done like this
last_item = maybe(lambda: self.stock.items[-1])

Now, for cases when the default value isn’t None (like on the question’s example), we can add the OR operator safely:

result = maybe(lambda: loads('{"value": true}')) or 'Oh no, explosions occurred!'

It’s a readable and easy solution to handle expected exceptions. I wish Python had something similar natively. As Fnord noted in his answer, the closest solution Python has is contextlib.suppress, but it has 2 limitations:

  1. It forces you to specify the exception type (safe, but usually unnecessary).
  2. It doesn’t resolve to a value (since the with statement has no return), which prevents you from using the result inside another expression. Notice that using suppress forces you to initialize result with the default string in a separate line.
Answered By: Leandro 86