Python: Как указать циклу for продолжить работу с функции? - PullRequest
17 голосов
/ 20 мая 2011

Иногда мне нужен следующий шаблон в цикле for. Иногда более одного раза в одном цикле:

    try:
        var = 'attempt to do something that may fail on a number of levels'
    except Exception, e:
        log(e)
        continue

Теперь я не вижу хорошего способа обернуть это в функцию, так как она не может return continue:

def attempt(this):
    try:
        return this
    except Exception, e:
        log(e)
        # 1. continue # <-- syntax error: continue not properly in loop or
        # 2. return continue # <-- invalid syntax
        # 3.
        return False # <-- this sort of works, but makes me feel powerless

Если бы я return False, чем я мог бы:

    var = attempt('to do something that may fail on a number of levels')
    if not var:
        continue

Но я не чувствую, что это справедливо. Я хочу указать циклу for continue (или подделать его) из функции attempt.

Ответы [ 9 ]

9 голосов
/ 20 мая 2011

Python уже имеет очень хорошую конструкцию для этого, и он не использует continue:

for i in range(10):
    try:
        r = 1.0 / (i % 2)
    except Exception, e:
        print(e)
    else:
        print(r)

Я бы не стал вкладывать больше, чем это, иначе ваш код скоро появитсястановится очень уродливым.

В вашем случае я бы, вероятно, сделал что-то более похожее на это, так как гораздо проще выполнить модульное тестирование отдельных функций, а flat лучше, чем вложенные :

#!/usr/bin/env python

def something_that_may_raise(i):
    return 1.0 / (i % 2)

def handle(e):
    print("Exception: " + str(e))

def do_something_with(result):
    print("No exception: " + str(result))

def wrap_process(i):
    try:
        result = something_that_may_raise(i)
    except ZeroDivisionError, e:
        handle(e)
    except OverflowError, e:
        handle(e) # Realistically, this will be a different handler...
    else:
        do_something_with(result)

for i in range(10):
    wrap_process(i)

Не забывайте всегда поймать определенные исключения .Если вы не ожидали выброса определенного исключения, вероятно, небезопасно продолжать цикл обработки.

Редактировать следующие комментарии:

Если вы действительно нене хочу обрабатывать исключения, которые я все еще считаю плохой идеей, а затем перехватывать все исключения (except:) и вместо handle(e) просто pass.В этот момент wrap_process() закончится, пропуская else: -блок, где выполняется настоящая работа, и вы перейдете к следующей итерации своего for -цикла.

Помните, Ошибки никогда не должны проходить бесшумно .

5 голосов
/ 20 мая 2011

Вся идея исключений заключается в том, что они работают на нескольких уровнях косвенности, т. Е. Если у вас есть ошибка (или любое другое исключительное состояние) глубоко в вашей иерархии вызовов, вы все равно можете поймать ее более высокий уровень и обрабатывать его правильно.

В вашем случае, скажем, у вас есть функция try (), которая вызывает функции предпринимают попытки2 () и попытки 3 () по иерархии вызовов, а попытка 3 () может столкнуться с исключительным состоянием, которое должно привести к завершению основного цикла:

class JustContinueException(Exception):
    pass

for i in range(0,99):
    try:
        var = attempt() # calls attempt2() and attempt3() in turn
    except JustContinueException:
        continue # we don't need to log anything here
    except Exception, e:
        log(e)
        continue

    foo(bar)

def attempt3():
    try:
        # do something
    except Exception, e:
        # do something with e, if needed
        raise # reraise exception, so we catch it downstream

Вы даже можете сами создать фиктивное исключение, которое просто приведет к завершению цикла и даже не будет зарегистрировано.

def attempt3():
    raise JustContinueException()
3 голосов
/ 20 мая 2011

Может быть, вы хотите продолжить? Вы могли бы пойти и посмотреть, как Эрик Липперт объясняет их (если вы готовы взорвать свой ум, но в Python это может выглядеть примерно так:

def attempt(operation, continuation):
    try:
        operation()
    except:
        log('operation failed!')
    continuation()

Внутри вашего цикла вы можете сделать:

attempt(attempt_something, lambda: foo(bar)) # attempt_something is a function
2 голосов
/ 20 мая 2011

Подумайте, что вы отображаете foo на все элементы, на которых работал attempt. Итак, attempt - это фильтр, и его легко написать в виде генератора:

def attempted( items ):
    for item in items:
        try:
            yield attempt( item )
        except Exception, e:
            log(e)

print [foo(bar) for bar in attempted( items )]
1 голос
/ 05 сентября 2012

Помимо контекста, я просто хочу кратко ответить на вопрос.Нет, функция не может continue цикл, в который она может быть вызвана. Это потому, что она не имеет информации об этом контексте.Кроме того, это подняло бы совершенно новый класс вопросов, например, что произойдет, если эта функция вызывается без окружающего цикла для обработки этого continue?

НО функция может сигнализировать различнымиозначает, что он хочет, чтобы вызывающий continue любой цикл, который он выполняет в настоящее время.Одним из средств, конечно, является возвращаемое значение.Верните False или None, чтобы сигнализировать это, например.Другой способ сообщить об этом - поднять специальный Exception:

class ContinuePlease(Exception): pass

def f():
    raise ContinuePlease()

for i in range(10):
    try:
        f()
    except ContinuePlease:
        continue
1 голос
/ 21 мая 2011

Я бы не стал публиковать второй ответ, но это альтернативный подход, если вам действительно не нравится мой первый ответ .

Помните, что функция может возвращать tuple.

#!/usr/bin/env python

def something_that_mail_fail(i):
    failed = False
    result = None
    try:
        result = 1.0 / (i % 4)
    except:
        failed = True # But we don't care
    return failed, result

for i in range(20):
    failed, result = something_that_mail_fail(i)
    if failed:
        continue
    for rah in ['rah'] * 3:
        print(rah)
    print(result)

Я утверждаю, что try ... except ... else - это путь, и вы не должны молча игнорировать ошибки. Предостережение emptor и все такое.

1 голос
/ 20 мая 2011

Вы можете использовать это:

for l in loop:
  attempt() and foo(bar)

, но вы должны убедиться, что попытка () возвращает True или False.

Действительно, хотя ответ Джонсивеба, вероятно, лучше.

0 голосов
/ 27 ноября 2014

поместите цикл for вне try, кроме блока ... simple ...; -)

import sys
if '3.4' in sys.version:
    from termcolor import colored</p>

<p>def list_attributes(module_name):
    '''Import the module before calling this func on it.s '''
    for index, method in enumerate(dir(module_name)):
        try:
                 method = str(method)
                 module = 'email'
                 expression = module + '.' + method
                 print('*' * len(expression), '\n')
                 print( str(index).upper() + '. ',colored( expression.upper(), 'red'),
                       ' ', eval( expression ).<strong>dir</strong>() , '...' , '\n'<em>2 )
                 print('</em>' * len(expression), '\n')
                 print( eval( expression + '.<strong>doc</strong>' ),  '\n'*4,
                   'END OF DESCRIPTION FOR: ' + expression.upper(), '\n'*4)
        except (AttributeError, NameError):
            continue
        else:
            pass
        finally:
            pass
0 голосов
/ 21 мая 2011

Редактировать: убрал всю ту глупость, которую я сказал ...

Последний ответ - переписать все это, чтобы мне не нужно было так кодировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...