gosuto
gosuto

Reputation: 5741

Pythonic way for a conditional return in a function

I have a couple of functions that return either a number or None. I want my wrapper function to return the first result that is not None. Is there any other way to do this than as following?

def func1():
    return None
def func2():
    return 3
def func3():
    return None

def wrapper():
    result = func1()
    if result is not None:
        return result
    result = func2()
    if result is not None:
        return result
    result = func3()
    if result is not None:
        return result

I am aware of return func1() or alternative; which returns the outcome of func1() unless it is None, then alternative is returned. In the most optimal situation I do something like (pseudo code):

return func1() or continue
return func2() or continue
return func3() or continue

Upvotes: 2

Views: 192

Answers (4)

chepner
chepner

Reputation: 530922

You need a loop which will return the first non-None value it finds.

def wrapper():
    for f in [func1, func2, func3]:
        result = f()
        if result is not None:
            return result

If every function returns None, wrapper will as well by reaching the end of the body without an explicit return.

Shortened slightly in Python 3.8 or later,

def wrapper():
    for f in [func1, func2, func3]:
        if (result := f()) is not None:
            return result

You can also make use of the any function:

def wrapper():
    if any((result := f()) is not None for f in [func1, func2, func3]):
        return result

(I prefer the explicit loop; YMMV.)

Upvotes: 4

Alain T.
Alain T.

Reputation: 42143

You could combine map, filter and next:

def func1():
    print("executed func1()")
    return None
def func2():
    print("executed func2()")
    return 3
def func3():
    print("executed func3()")
    return None

functions = (func1,func2,func3)
call      = lambda f:f()
result    = next(filter(None,map(call,functions)),None)

print("result:",result)

output:

executed func1()
executed func2()
result 3

func 3 is not executed

If your functions have parameters you can still use this approach by building the list of functions with lambdas:

def func1(p1):
    print(f"executed func1({p1})")
    return None
def func2(p2a,p2b):
    print(f"executed func2{(p2a,p2b)}")
    return 3
def func3(p3):
    print(f"executed func3({p3})")
    return None

functions = [
               lambda: func1(10),
               lambda: func2("X",99),
               lambda: func3(3.5)
            ]
call      = lambda f:f()
result    = next(filter(None,map(call,functions)),None)

print(result)

output:

executed func1(10)
executed func2('X', 99)
3

Upvotes: 1

khelwood
khelwood

Reputation: 59096

If there's only three functions, and you don't care about distinguishing None from other falsey values, you can use

return func1() or func2() or func3()

If there are lots of functions, or if you want to be more discriminating about the return values, then you could put them all in a list and use

return next(x for x in (f() for f in funcs) if x is not None)

If you want to cope with all of your functions returning None, you can add a default value for next, e.g.

return next((x for x in (f() for f in funcs) if x is not None), None)

Upvotes: 2

Riccardo Bucco
Riccardo Bucco

Reputation: 15364

You can do this:

def wrapper():
    functions = [func1, func2, func3]
    return next(filter(lambda result: result is not None,
                       f() for f in functions),
                None)

It returns None if all the functions return None.

Upvotes: 0

Related Questions