9

The following example is very simple. I want to execute map() with a function which can raise Exception. It will be more clearly with an example :

number_list = range(-2,8)

def one_divide_by(n):
    return 1/n

try:
    for number, divide in zip(number_list, map(one_divide_by, number_list)):
        print("%d : %f" % (number, divide))
except ZeroDivisionError:
    # Execution is stopped. I want to continue mapping
    pass

When I execute this code I get :

-2 : -0.500000
-1 : -1.000000

It's due to the 0 in my list. I don't want remove this 0 (because in real case I can't know first if I will get Exception). Do you know how to continue mapping after the exception ?

Samuel Dauzon
  • 9,917
  • 12
  • 56
  • 89
  • if you know what value to substitute for 'infinite' then use `try: return 1/n; except ZeroDivisionError: return yourvalue` – Pynchia Sep 25 '15 at 10:35

4 Answers4

5

you could catch the exception in your function (instead of in the for loop) and return None (or whatever you choose) if ZeroDivisionError is raised:

def one_divide_by(n):
    try:
        return 1/n
    except ZeroDivisionError:
        return None

if you choose to return None you need to adapt your format string; None can not be formatted with %f.

other values you could return (and that would be compatible with your string formatting) are float('inf') (or float('-inf') depending on the sign of your numerator) or float('nan') - "infinity" or "not a number".

here you will find some of the caveats of using float('inf').

Community
  • 1
  • 1
hiro protagonist
  • 40,708
  • 13
  • 78
  • 98
  • 1
    I think returning `float('nan')` is probably the best option here. But I guess that it should be mentioned that you need to use [math.isnan()](https://docs.python.org/3/library/math.html?highlight=math.isnan#math.isnan) to test `nan`. – PM 2Ring Sep 25 '15 at 11:01
4

You can move the try/except block inside the function. Example -

def one_divide_by(n):
    try:
        return 1/n
    except ZeroDivisionError:
        return 0   #or some other default value.

And then call this normally, without a try/except block -

for number, divide in zip(number_list, map(one_divide_by, number_list)):
    print("%d : %f" % (number, divide))
Anand S Kumar
  • 82,977
  • 18
  • 174
  • 164
2

If you can replace map instead of changing the definition of your function one_divide_by, I would recommend more_itertools.map_except:

from more_itertools import map_except

number_list = range(-2,8)

def one_divide_by(n):
    return 1/n

for number, divide in map_except(
        lambda number: (number, one_divide_by(number)),
        number_list,
        ZeroDivisionError
):
    print(number, ':', divide)

This prints, as expected:

-2 : -0.5
-1 : -1.0
1 : 1.0
2 : 0.5
3 : 0.3333333333333333
4 : 0.25
5 : 0.2
6 : 0.16666666666666666
7 : 0.14285714285714285

The function more_itertools.map_except accepts a function, an iterable as inputs (just like the built-in map) and expected exceptions. When one of those exceptions is raised during the mapping process, the element is simply skipped.

This package can be installed using pip install more-itertools, for pip users, or conda install -c conda-forge more-itertools, for Conda users.

ruancomelli
  • 522
  • 6
  • 15
0

Another option is to define a helper function that accepts a handler for exceptions like this:

def map_e(function, *iterables, on_exception=None):
    for elements in zip(*iterables):
        try:
            yield function(*elements)
        except Exception as e:
            yield on_exception(elements, exception=e) \
                if callable(*on_exception) \
                else None


list_a = [1,  2, 5, 1]
list_b = [2, -2, 6, 6]
list_c = [2,  1, 0, 8]


def sum_then_divide(a, b, c):
    return (a + b) / c


def none_on_exception(*args, **kwargs):
    return None


result = list(
    map_e(
        sum_then_divide,
        list_a,
        list_b,
        list_c,
        on_exception=none_on_exception
    )
)

print(result)

No need to handle the exception in your main function, and no need to add external libraries.

UselesssCat
  • 1,929
  • 3
  • 17
  • 33