Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Define IFERROR function

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?

like image 338
Kalev Maricq Avatar asked Jul 02 '19 18:07

Kalev Maricq


People also ask

How do I use Iferror in Python?

What you need to look at is the very last line - ValueError. This is the type of error and changes depending on what went wrong. So to setup an IFERROR you simply add in a TRY and EXCEPT.

How do I return 0 if error?

You can also suppress this error by nesting your division operation inside the IFERROR function. Again, using A2/A3, you can use =IFERROR(A2/A3,0). This tells Excel if your formula evaluates to an error, then return 0, otherwise return the result of the formula.


2 Answers

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.

like image 165
melpomene Avatar answered Oct 03 '22 07:10

melpomene


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.

like image 42
TornaxO7 Avatar answered Oct 03 '22 09:10

TornaxO7