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