Reputation: 12672
I am looking for a better way to implement this sort of logic:
if not a():
if not b():
c()
b()
a()
Another form:
try:
a()
except:
try:
b()
a()
except:
c()
b()
a()
In words, "Try to run A. If we can't do A, we need to do B first. If we can't do B, we need to do C first, etc."
Upvotes: 2
Views: 127
Reputation: 8617
Create a function like fallback_until_success(func_list)
, where func_list = [a, b, c]
. If you have arguments, you can bind them e.g. by passing tuples of (func, *args, **kwargs)
.
Then you can go through the list in a while loop (including fallback-backtracking per iteration) until you get a success or hit the end of the list; if you dont get a success, return the last exception (or a list of exceptions).
However, this seems like a case where having an initial test to inform your code path is better than trying to do the damage first and backtracking. What you are doing is abusing exceptions as a message-passing service.
Update: well its too late now anyway, but here is a concrete example:
def fallback_until_success(func_list):
index = 0
results = []
exceptions = []
while (index < len(func_list)):
try:
print func_list[index::-1] # debug printing
for func_spec in func_list[index::-1]:
#func, args, kwargs = func_spec # args variant
#result = func(*args, **kwargs)
func = func_spec
result = func()
results.append(result)
break
except Exception, e:
exceptions.append(e)
index += 1
results = []
continue
break
return results, exceptions
# global "environment" vars
D = {
"flag1": False,
"flag2": False,
}
def a():
if not D["flag1"]:
failstr = "a(): failure: flag1 not set"
print failstr
raise Exception(failstr)
print "a(): success"
return D["flag1"]
def b():
if not D["flag2"]:
failstr = "b(): failure: flag2 not set"
print failstr
raise Exception(failstr)
else:
D["flag1"] = True
print "b(): success"
return D["flag2"]
def c():
D["flag2"] = True
print "c(): success"
return True
# args variant
#results, exceptions = fallback_until_success([(a, [], {}), (b, [], {}), (c, [], {})])
results, exceptions = fallback_until_success([a, b, c])
print results
print exceptions
The output:
[<function a at 0x036C6F70>]
a(): failure: flag1 not set
[<function b at 0x03720430>, <function a at 0x036C6F70>]
b(): failure: flag2 not set
[<function c at 0x037A1A30>, <function b at 0x03720430>, <function a at 0x036C6F70>]
c(): success
b(): success
a(): success
[True, True, True]
[Exception('a(): failure: flag1 not set',), Exception('b(): failure: flag2 not set',)]
Of course, this is based on exceptions, but you could modify this to base success/failure on return values also.
Upvotes: 1
Reputation: 152
Why expose all of that to the caller? Caller shouldn't know/care about the details of how a widget works.. Why not insulate the client code from the "guts" by doing something like:
do_stuff() # This is the only call you make directly
def do_stuff():
## commands for Step A, in this case
## try to update the changeset
result = False
# Do stuff and store result
if (result == False):
result = step_B()
return result
def step_B():
## Commands for Step B, in this case
## try to pull the repository
result = False
# Do stuff and store the result
if (result == False):
result = step_C()
return result
def step_C():
## Commands for Step C, in this case
## try to clone the repository
## and set `result' to True or False
result = False
# Do stuff and set `result'
return result
Upvotes: 0
Reputation: 12672
Based on Shao-Chuan Wang's answer, I think I might end up doing something like this:
any(all((a())),
all((b(), a())),
all((c(), b(), a())))
Upvotes: 0
Reputation: 306
This should work. Note that it if a fails, it will execute b,c,a. If b then fails, it will execute c,a,b -- that is, not in original order but should be good if order isn't of any particular preference.
ops = [a,b,c]
while op = ops.pop(0):
if (op()):
continue
ops.append(op)
Upvotes: 0
Reputation: 1030
Not sure if you feel 'better' about this one; here is an alternative. I believe some people like it and some people don't.
a() or (b(),a())[0] or (c(),b(),a())[0]
Here is validation test:
def a(ret):
print 'run a, a succeeded?', ret
return ret
def b(ret):
print 'run b, b succeeded?', ret
return ret
def c(ret):
print 'run c, c succeeded?', ret
return ret
And
a(False) or (b(False),a(False))[0] or (c(True),b(False),a(False))[0]
gives
run a, a succeeded? False
run b, b succeeded? False
run a, a succeeded? False
run c, c succeeded? True
run b, b succeeded? False
run a, a succeeded? False
And
a(False) or (b(True),a(False))[0] or (c(True),b(True),a(False))[0]
gives
run a, a succeeded? False
run b, b succeeded? True
run a, a succeeded? False
Upvotes: 1
Reputation: 216
How about:
while not a():
while not b():
c()
This only works as long as c()
is expected to eventually make b()
succeed (likewise for b()
and a()
), but this is a relatively common pattern for me.
Upvotes: 1