Malik Fassi
Malik Fassi

Reputation: 488

How to execute many functions one after the other

I'd like to execute many function after the other. Each function returns True or False. So if one function returns True, I'd like to execute the next one. etc...

All the functions don't have necessary the same arguments.

Now i have something like :

res=function1()
if res:
   res=function2()
   if res:
      res=function2()

And it goes on and on for like 20 functions. Is there a better way to do this ?

Thank you in advance...

Upvotes: 1

Views: 3957

Answers (4)

Ned Batchelder
Ned Batchelder

Reputation: 375574

There's no need to get too complicated:

res = function1()
res = res and function2()
res = res and function3()
...

This looks a little odd, but will do what you want, without having to twist around the function calls themselves into lists of dictionaries or something. It's just a longer way of writing Cairnarvon's answer:

res = (
   function1() and
   function2() and
   function3() and
   ...
   )

Upvotes: 1

abarnert
abarnert

Reputation: 365707

I'd probably use partial to make zero-argument functions that you can loop over (instead of some kind of structure with the function and its arguments):

functions = [
    functools.partial(func1, arg1a, arg1b),
    functools.partial(func2),
    functools.partial(func3, keyword_a=kwarg3a, keyword_b=kwarg3b)
]

Then, instead of putting it into a list and iterating over it, you can just call all:

retval = all(func() for func in (
    functools.partial(func1, arg1a, arg1b),
    functools.partial(func2),
    functools.partial(func3, keyword_a=kwarg3a, keyword_b=kwarg3b)
))

That will return False as soon as one of the functions returns False (or anything false-y), or run all of the functions and return True if they all return True (or anything true-y). As the docs say, it's equivalent to:

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

It's worth comparing the partials to the tuples in the other answer, which serve as pseudo-partials, both in how they're defined and in how they're called:

f1 = functools.partial(func, arg1, arg2, kw1=kwarg1, kw2=kwarg2)
f2 = (func1, (arg1a, arg1b), {'kw1': kwarg1, 'kw2': kwarg2 })

f1()
f2[0](*f2[1], **f2[2])

Obviously you can (and should, as aemdy's answer does) make the call more readable with tuple-unpacking, but it's still never going to be as simple as using a real partial.

Upvotes: 4

aemdy
aemdy

Reputation: 3802

Well, you can define your own way to do this, but I would do that like that:

my_functions = (
    (my_func1, [2, 5], {'kwarg1': 'val1'}),
    # ...
)

for function, args, kwargs in my_functions:
    if not function(*args, **kwargs):
        break

Edited according to the comment. Great insight!

Upvotes: 8

Cairnarvon
Cairnarvon

Reputation: 27762

You can take advantage of the short-circuiting behaviour of the and operator:

function1() and function2() and function3() and ...

function2 will only be called if function1 returned True, function3 will only be called if function1 and function2 returned True, and so on.

It's not necessarily very Pythonic, though.

Since you're always assigning to res, you can also just keep the ifs flat:

res = function1()

if res:
    res = function2()

if res:
    res = function3()

That could considered more readable, but it does waste a lot of vertical space. At least you aren't nesting ifs two dozen deep, though.

Upvotes: 2

Related Questions