我正在寻找一种更好的方法来实现这种逻辑:
if not a():
if not b():
c()
b()
a()
另一种形式:
try:
a()
except:
try:
b()
a()
except:
c()
b()
a()
换句话说,“尝试运行A。如果我们不能做A,我们需要先做B。如果我们不能做B,我们需要先做C,等等。”
我正在寻找一种更好的方法来实现这种逻辑:
if not a():
if not b():
c()
b()
a()
另一种形式:
try:
a()
except:
try:
b()
a()
except:
c()
b()
a()
换句话说,“尝试运行A。如果我们不能做A,我们需要先做B。如果我们不能做B,我们需要先做C,等等。”
创建一个函数,如fallback_until_success(func_list)
, where func_list = [a, b, c]
。如果你有参数,你可以绑定它们,例如通过传递(func, *args, **kwargs)
.
然后你可以在一个while循环中遍历列表(包括每次迭代的回退回溯),直到你获得成功或到达列表的末尾;如果您没有成功,请返回最后一个异常(或异常列表)。
但是,这似乎是这样一种情况,即进行初始测试以告知您的代码路径比尝试先造成损害并回溯更好。您正在做的是滥用异常作为消息传递服务。
更新:无论如何,现在为时已晚,但这是一个具体的例子:
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
输出:
[<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',)]
当然,这是基于异常的,但您也可以将其修改为基于返回值的成功/失败。
怎么样:
while not a():
while not b():
c()
这只适用于c()
预期最终b()
成功的情况(对于b()
and也是如此a()
),但这对我来说是一种相对常见的模式。
不确定您是否对此感觉“更好”;这是另一种选择。我相信有些人喜欢,有些人不喜欢。
a() or (b(),a())[0] or (c(),b(),a())[0]
这是验证测试:
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
和
a(False) or (b(False),a(False))[0] or (c(True),b(False),a(False))[0]
给
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
和
a(False) or (b(True),a(False))[0] or (c(True),b(True),a(False))[0]
给
run a, a succeeded? False
run b, b succeeded? True
run a, a succeeded? False
这应该有效。请注意,如果 a 失败,它将执行 b,c,a。如果 b 然后失败,它将执行 c,a,b ——也就是说,不是按照原始顺序,但如果顺序没有任何特别的偏好,应该是好的。
ops = [a,b,c]
while op = ops.pop(0):
if (op()):
continue
ops.append(op)
根据 Shao-Chuan Wang 的回答,我想我最终可能会做这样的事情:
any(all((a())),
all((b(), a())),
all((c(), b(), a())))
为什么要将所有这些都暴露给调用者?调用者不应该知道/关心小部件如何工作的细节。为什么不通过执行以下操作将客户端代码与“胆量”隔离开来:
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