5

I'm trying to define my own IFERROR function in python like in Excel. (Yes, I know I can write try/except. I'm just trying to create an inline shorthand for a try/except pattern I often use.) The current use case is trying to get several attributes of some remote tables. The module used to connect to them gives a variety of errors and if that happens, I simply want to record that an error was hit when attempting to get that attribute.

What I've tried: A search revealed a number of threads, the most helpful of which were:

Frequently repeated try/except in Python

Python: try-except as an Expression?

After reading these threads, I tried writing the following:

>>> def iferror(success, failure, *exceptions):
...     try:
...         return success
...     except exceptions or Exception:
...         return failure
...
>>> iferror(1/0,0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

I also tried using a context manager (new to me):

>>> from contextlib import contextmanager as cm
>>> @cm
... def iferror(failure, *exceptions):
...     try:
...         yield
...     except exceptions or Exception:
...         return failure
...
>>> with iferror(0,ZeroDivisionError) as x:
...     x=1/0
...
>>> print(x)
None

Is there a way to define a function which will perform a predefined try/except pattern like IFERROR?

2
  • 1
    return success() and iferror(lambda: 1/0, 0)? Commented Jul 2, 2019 at 18:17
  • I still get a division by zero error using that. If it works for you, can you post the full code that is working? Commented Jul 2, 2019 at 19:47

2 Answers 2

6

The problem with iferror(1/0,0) is that function arguments are evaluated before the function is entered (this is the case in most programming languages, the one big exception being Haskell). No matter what iferror does, 1/0 runs first and throws an error.

We must somehow delay the evaluation of 1/0 so it happens inside the function, in the context of a try block. One way is to use a string (iferror('1/0', 1)) that iferror can then eval. But eval should be avoided where possible, and there is a more light-weight alternative: Function bodies are not evaluated until the function is called, so we can just wrap our expression in a function and pass that:

def iferror(success, failure, *exceptions):
    try:
        return success()
    except exceptions or Exception:
        return failure

def my_expr():
    return 1/0

print(iferror(my_expr, 42))
42

The crucial part here is that we don't call my_expr directly. We pass it as a function into iferror, which then invokes success(), which ends up executing return 1/0.

The only problem is that we had to pull the function argument (1/0) out of the normal flow of code and into a separate function definition, which we had to give a name (even thought it's only used once).

These shortcomings can be avoided by using lambda, which lets us define single-expression functions inline:

def iferror(success, failure, *exceptions):
    try:
        return success()
        #             ^^
    except exceptions or Exception:
        return failure

print(iferror(lambda: 1/0, 42))
#             ^^^^^^^
42

[Live demo]

Compared to your original attempt, only two changes were necessary: Wrap the expression in a lambda:, which delays evaluation, and use () in try: return success() to call the lambda, which triggers evaluation of the function body.

Sign up to request clarification or add additional context in comments.

1 Comment

This is a general technique that also works in e.g. Perl or JavaScript (but the latter doesn't think 1/0 is an error and just returns Infinity).
2

Well I found a way but I'm not quite sure, if it's that what you are looking for.
First of all the error occurs if you call the function, as a result your function doesn't start at all! So I gave the first parameter of the function a string.
So the function gets your "test" and "controls" it with eval(). Here is my Code:

def iferror(success: str, failure, *exceptions):
    try:
        # Test
        return eval(success)

    except exceptions or Exception:
        return failure    

iferror("1/0", "Hi there!")

I hope I could hope you.

1 Comment

That's an interesting idea, using eval. Obviously, not everything will work with eval, but I think that can be my back-up plan. If no one can come up with a different way, I'll pick this as the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.