Python 肾盂道;如果Y失败,则在Y之前运行X;?
我正在寻找更好的方法来实现这种逻辑:Python 肾盂道;如果Y失败,则在Y之前运行X;?,python,Python,我正在寻找更好的方法来实现这种逻辑: 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\u直
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\u直到成功(func\u list)
,其中func\u 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
def fallback\u直到成功(功能列表):
索引=0
结果=[]
例外情况=[]
而(索引
输出:
[<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',)]
[]
a():失败:未设置flag1
[, ]
b():故障:未设置标志2
[, ]
c():成功
b():成功
成功
[真的,真的,真的]
[异常('a():故障:未设置flag1',),异常('b():故障:未设置flag2',)]
当然,这是基于异常的,但您也可以将其修改为基于返回值的成功/失败。如何:
while not a():
while not b():
c()
这只适用于
c()
最终会使b()
成功的情况(同样适用于b()
和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失败,它将执行b、c、a。如果b然后失败,它将执行c,a,b——也就是说,不是按原始顺序执行,但如果顺序不是特别喜欢的,它应该是好的
ops = [a,b,c]
while op = ops.pop(0):
if (op()):
continue
ops.append(op)
基于王少川的观点,我想我可能会做这样的事情:
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
您正在引发异常后运行的异常块中运行
a
和b
。这对我来说是个坏主意。所以如果a()
失败,b()
可能会以某种方式成功,从而使a()
能够运行?这让我畏缩。。。你心目中的确切用例是什么?你的确切问题/情况是什么?我想我可以想出一个例子:a
尝试打开一个文件,b
修复没有访问权限的权限。你为什么不直接测试repo的存在,等等。,然后根据您找到的状态显式执行所需的选项?也就是说,在确认回购存在之前,不要尝试检查状态。这样,如果出现错误,它将是一个真正的错误,您可以让它传播。如果初始测试需要“很长”的时间怎么办?我的答案没有意义吗?我提出的解决方案基本上是语法糖,用于你在答案中发布的try-except链。不知道你为什么认为会慢一点?我是在回应你的“然而…”一段。我认为你的答案的第一部分是正确的,但我不认为它会返回到哪里去重试func\u列表中的早期项目。我喜欢这样,但是如果由于某种原因a
或b
总是失败,我们会得到一个无限循环。
run a, a succeeded? False
run b, b succeeded? True
run a, a succeeded? False
ops = [a,b,c]
while op = ops.pop(0):
if (op()):
continue
ops.append(op)
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