2

我正在寻找一种更好的方法来实现这种逻辑:

if not a():
    if not b():
        c()
        b()
    a()

另一种形式:

try:
   a()
except:
   try:
      b()
      a()
   except:
      c()
      b()
      a()

换句话说,“尝试运行A。如果我们不能做A,我们需要先做B。如果我们不能做B,我们需要先做C,等等。”

4

6 回答 6

1

创建一个函数,如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',)]

当然,这是基于异常的,但您也可以将其修改为基于返回值的成功/失败。

于 2013-04-04T01:04:32.550 回答
1

怎么样:

while not a():
    while not b():
        c()

这只适用于c()预期最终b()成功的情况(对于b()and也是如此a()),但这对我来说是一种相对常见的模式。

于 2013-04-04T01:24:00.060 回答
1

不确定您是否对此感觉“更好”;这是另一种选择。我相信有些人喜欢,有些人不喜欢。

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
于 2013-04-04T01:27:47.917 回答
0

这应该有效。请注意,如果 a 失败,它将执行 b,c,a。如果 b 然后失败,它将执行 c,a,b ——也就是说,不是按照原始顺序,但如果顺序没有任何特别的偏好,应该是好的。

ops = [a,b,c]

while op = ops.pop(0):
  if (op()): 
    continue
  ops.append(op)
于 2013-04-04T01:39:54.967 回答
0

根据 Shao-Chuan Wang 的回答,我想我最终可能会做这样的事情:

any(all((a())),
    all((b(), a())),
    all((c(), b(), a())))
于 2013-04-04T01:56:42.270 回答
0

为什么要将所有这些都暴露给调用者?调用者不应该知道/关心小部件如何工作的细节。为什么不通过执行以下操作将客户端代码与“胆量”隔离开来:

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
于 2013-04-04T02:06:42.407 回答