2013-04-04 4 views
2

Ищу лучший способ реализации такого рода логики:Pythonic способ «запускать X до Y, если Y не удается»?

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

Другая форма:

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

В словах, «Попробуйте запустить A. Если мы не можем сделать, нам сначала нужно сделать B. Если мы не сможем сделать B, нам нужно сначала сделать C и т. д. »

+0

Вы работаете '' Ā' и б 'в блоке исключений, который запускается после того, как они вызвали исключение. Это пахнет плохой идеей для меня. – tacaswell

+2

Какова ваша * точная * проблема/ситуация? – Blender

+0

Итак, если 'a()' fail, 'b()' может каким-то образом преуспеть, что позволяет запускать 'a()'? Это заставляет меня съеживаться ... Каков конкретный случай использования, который вы имеете в виду? –

ответ

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 (включая резервное обратное отслеживание на итерацию), пока не получите успех или не попадете в конец списка; если вы не достигли успеха, верните последнее исключение (или список исключений).

Однако это похоже на случай, когда первоначальный тест для информирования о вашем пути кода лучше, чем попытка сначала нанести урон и отступить. То, что вы делаете, является злоупотреблением исключениями как службой передачи сообщений.

Update: хорошо его слишком поздно так или иначе, но вот конкретный пример:

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

Что делать, если начальный тест занимает «длинное» время? –

+2

Является ли мой ответ нецелесообразным? Решение, которое я предложил, в основном является синтаксическим сахаром для цепочки try-except, которую вы указали в своем ответе. Не знаете, почему вы думаете, что это будет медленнее? –

+0

Я отвечал на ваш абзац «Однако ...». Я думаю, что первая часть вашего ответа правильная, но я не вижу, куда она вернется, чтобы повторить предыдущие элементы в 'func_list'. –

1

Как насчет:

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

Это работает только до тех пор, как c(), как ожидается, в конечном итоге сделать b() успеха (также для b() и a()), но это довольно распространенная картина для меня.

+0

Мне это нравится, но если по какой-то причине 'a' или' b' всегда терпят неудачу, мы получаем бесконечный цикл. –

0

Это должно работать. Обратите внимание, что если сбой, он выполнит b, c, a. Если b не выполняется, он выполнит c, a, b - то есть не в исходном порядке, но должен быть хорошим, если порядок не имеет особого предпочтения.

ops = [a,b,c] 

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

основы Shao-Chuan Ван answer, я думаю, что я мог бы в конечном итоге делаю что-то вроде этого:

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

Почему разоблачить все, что для вызывающего абонента? Caller не должны знать/заботиться о деталях как работает виджет .. Почему бы не изолировать код клиента от «внутренностей», делая что-то вроде:

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 
Смежные вопросы