Pythonic的方式“如果Y失败则在Y之前运行X”?

时间:2013-04-04 00:55:41

标签: 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等等。”

6 个答案:

答案 0 :(得分:1)

创建像fallback_until_success(func_list)这样的函数,其中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',)]

当然,这是基于异常的,但您可以修改它以使返回值的成功/失败基础。

答案 1 :(得分:1)

怎么样:

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

只有c()最终会使b()成功(同样适用于b()a()),此功能才有效,但这对我来说是一种相对常见的模式

答案 2 :(得分: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

答案 3 :(得分:0)

这应该有效。请注意,如果失败,它将执行b,c,a。如果b然后失败,它将执行c,a,b - 即,不是原始顺序,但如果订单不是任何特定的偏好,那么应该是好的。

ops = [a,b,c]

while op = ops.pop(0):
  if (op()): 
    continue
  ops.append(op)

答案 4 :(得分:0)

基于王少韶的answer,我想我最终可能会做这样的事情:

any(all((a())),
    all((b(), a())),
    all((c(), b(), a())))

答案 5 :(得分: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