23

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.
2Cubed
  • 3,091
  • 6
  • 21
  • 40
  • 2
    Why not put the standard `try..except` into the function? – TigerhawkT3 Apr 16 '16 at 23:46
  • @Slayer Interestingly, Python will actually use a string (or any other object, in fact) as an "alternate" for `or`. `0 or "Does not exist."` will return `"Does not exist."`. (I have tested it.) – 2Cubed Apr 16 '16 at 23:48
  • @TigerhawkT3 I do not have access to the internals of the `divide_one_by` function - assume that it is defined in a separate module which I am using as a dependency. – 2Cubed Apr 16 '16 at 23:49
  • @2Cubed: no reason to be surprised. `0 OR x` evaluates to `x` for all values of `x`, except`0` itself. – Jongware Apr 16 '16 at 23:49
  • So write a wrapper function, perhaps? – TigerhawkT3 Apr 16 '16 at 23:49
  • @RadLexus - It evaluates to `x` when `x` is `0` as well. It'll simply be `0`. – TigerhawkT3 Apr 16 '16 at 23:50
  • @TigerhawkT3: does that not evaluate to `False` instead? (Testing..) Oh I see it indeed does not. :) `print 0 or False` *does*, though, proving my point on *anything* for `x`. Even 0. – Jongware Apr 16 '16 at 23:52
  • @TigerhawkT3: Good idea. Thanks! – 2Cubed Apr 16 '16 at 23:53
  • 3
    There is a [PEP](http://legacy.python.org/dev/peps/pep-0463/#proposal) to add something like this, but it hasn't been accepted yet. – chepner Apr 17 '16 at 00:26
  • suppress maybe useful for some cases https://stackoverflow.com/a/52020518/8583496 – Alexey Shrub Dec 20 '19 at 07:42

3 Answers3

33

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!"
mx0
  • 5,600
  • 10
  • 45
  • 51
pppery
  • 3,550
  • 19
  • 28
  • 41
  • You have a great suggestion. I am trying to create a similar workaround for my problem but the code is unable to work. I have detailed the problem at https://stackoverflow.com/q/56916092/6701627. I will be thankful if you could check the problem and provide some suggestion on it. – thepunitsingh Jul 06 '19 at 17:03
2

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)
Stephen Rauch
  • 44,696
  • 30
  • 102
  • 125
  • 1
    yeah but that's so horribly unpythonic... it's like putting an image of a website on a website instead of the actual html itself – sp4c38 Jun 22 '21 at 22:01
1

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
Fnord
  • 4,744
  • 4
  • 26
  • 45