2013-04-04 78 views
2

我正在尋找一種更好的方式來實現這種邏輯:Pythonic方式「如果Y失敗,在Y之前運行X」?

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

另一種形式:

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

在口頭上,「嘗試運行A.如果我們不能做到,我們需要先做B,如果我們不能做B,我們需要先做C,等等。「

+0

您正在運行'了'和'b '在引發異常之後運行的異常塊中。這對我來說是個不好的主意。 – tacaswell 2013-04-04 00:57:25

+2

你的確切*問題/情況是什麼? – Blender 2013-04-04 00:58:28

+0

所以如果'a()'失敗,'b()'可能會以某種方式成功運行'a()'?這讓我感到害怕......你有什麼確切的用例? – 2013-04-04 00:58:28

回答

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 
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

如果最初的測試需要「很長」的時間會怎麼樣? – 2013-04-04 01:08:00

+2

我的回答沒有意義嗎?我提出的解決方案基本上是您在答案中發佈的try-except鏈的語法糖。不知道爲什麼你認爲它會更慢? – 2013-04-04 01:17:59

+0

我回應了你的「然而...」段落。我認爲你的答案的第一部分是正確的,但我不知道它會在哪裏回溯到重試'func_list'中的早期項目。 – 2013-04-04 01:24:26

1

如何:

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

這僅適用,只要c()預計最終使b()(同樣爲b()a())成功,但是這對我來說是一種比較常見的模式。

+0

我喜歡這個,但如果由於某種原因'a'或'b'總是失敗,我們會得到一個無限循環。 – 2013-04-04 01:34:46

0

這應該工作。請注意,如果失敗,它將執行b,c,a。如果b然後失敗,它將執行c,a,b - 也就是說,不是原來的順序,但是如果順序沒有任何特定的偏好,它應該是好的。

ops = [a,b,c] 

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

基於邵川王的answer,我想我可能最終會做這樣的事情:

any(all((a())), 
    all((b(), a())), 
    all((c(), b(), a()))) 
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 
相關問題