Reputation: 488
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
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
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 partial
s to the tuple
s 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
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
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 if
s 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 if
s two dozen deep, though.
Upvotes: 2