Как остановить функцию в питоне
Перейти к содержимому

Как остановить функцию в питоне

  • автор:

Как остановить выполнение функции в python?

Делаю расширение для уже существующей крупной программы.
Необходимо остановить выполнение функции по прошествии некоторого таймаута.

сlass TimeoutError(Exception): pass def timeout(seconds=10, error_message=os.strerror(errno.ETIME)): def decorator(func): def _handle_timeout(signum, frame): raise TimeoutError(error_message) def wrapper(*args, **kwargs): signal.signal(signal.SIGALRM, _handle_timeout) signal.alarm(seconds) try: result = func(*args, **kwargs) finally: signal.alarm(0) return result return wraps(func)(wrapper) return decorator . @timeout(5) def some_method() . pass try: some_method() except TimeoutError: error_handling()

На что получаю ошибку:

Failed (): signal only works in main thread

В общем, смысл ошибки понятен, но изменить ничего в самой программе не могу, соответственно мое расширение всегда будет не в main thread.

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

  • Вопрос задан более трёх лет назад
  • 13101 просмотр

Как остановить функцию в питоне

как выйти из функции (прервать ее выполнение)?

есть функция следующего вида:

private function zzz():void { if (z == 0) { y = 1; } else if (z == 1) { y = 2; } i = y + 5; }

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

private function zzz():void { if (z == 0) { y = 1; } else if (z == 1) { y = 2; i = y + 10; //exit } i = y + 5; }

Как правильно остановить корутину

Корутина работает благодаря тому, что event loop раз за разом вызывает её метод coroutine.send(…) . Если перестать вызывать send , то и корутина перестанет работать. Это простой и изящный способ остановить корутину, но не все так просто.

Рассмотрим пример. У нас есть компьютерная игра, которая хранит в базе данных достижения игрока. Функция работает всю игру, и каждые 5 секунд сохраняет в БД список достижений:

async def save_achivements(user_id): connection = open_connection() while True: achivements = get_achivements(user_id) await connection.send(key=user_id, value=achivements) await asyncio.sleep(5) connection.close() 

С этим кодом есть проблема. Если случится ошибка внутри while True: , то до закрытия соединения с БД дело так и не дойдет. Возникнет исключение, нормальный поток исполнения команд прервется и connection.close() никто не вызовет. От такой проблемы спасает try finally :

async def save_achivements(user_id): connection = open_connection() try: while True: achivements = get_achivements(user_id) await connection.send(key=user_id, value=achivements) await asyncio.sleep(5) finally: connection.close() 

Теперь в случае ошибки сработает код внутри finally и соединение будет закрыто вызовом connection.close() , ни смотря ни на что. Это победа!

А что произойдет, если корутину save_achivements(…) просто перестанут вызывать? Игрок прервал игру, корутина save_achivements(…) перестала быть нужной, поэтому её выкинули из event loop и соединение осталось незакрытым. Простой и элегантный способ остановки корутины сломал нам try finally .

Из-за этой проблемы у корутин есть еще один метод — coroutine.throw(…) . Он принимает объект исключения — coroutine.throw(exc_obj) — и забрасывает его внутрь корутины. Исключение всплывает по стеку вызовов корутин, как в обычных синхронных функциях.

CancelledError

Event loop, встроенный в библиотеку asyncio никогда не прерывает работу корутин, вместо этого он просит их остановиться самим. Его просьба — это исключение CancelledError . Его можно перехватить и обработать, как обычное исключение. Воспользуемся этим фактом, чтобы перед выходом из игры записать в БД дату последнего сохранения:

import time import asyncio async def save_achivements(user_id): connection = open_connection() try: while True: achivements = get_achivements(user_id) await connection.send(key=user_id, value=achivements) await asyncio.sleep(5) except asyncio.CancelledError: timestamp = time.time() await connection.send(key=user_id, value=achivements) await connection.send(key='last_update', value=timestamp) # отпускаем перехваченный CancelledError raise finally: connection.close() 

Внутри asyncio even loop происходит примерно следующее:

 coroutine = save_achivements(user_id) coroutine.send(...) coroutine.send(...) ... coroutine.throw(asyncio.CancelledError()) coroutine.send(...) coroutine.send(...) ... 

Обратите внимание, что CancelledError не заблокировал работу event loop. Метод coroutine.send(…) продолжает вызываться, авейты исправно работают, а значит, завершение работы корутины тоже может быть асинхронным с вызовами await connection.send(…) .

Никто не может остановить корутину, пока та сама не пожелает. Внешний код будет терпеливо ждать, когда же корутина закончит работу и, чтобы он узнал об этом, в конце отпускаем перехваченный CancelledError . Event loop asyncio поймает это исключение и уведомит всех, кто ждал этого завершения. Подробнее читайте в документации по asyncio.Task.

Остановить корутину может только сама корутина

StopIteration vs CancelledError

StopIteration случается, когда корутина уже дошла до return и успела завершить свою работу. Это исключение нельзя перехватить внутри корутины, оно существует только снаружи.

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

CancelledError просит об остановке, а StopIteration — её констатирует

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.

Использование выражений Break, Continue и Pass при работе с циклами в Python 3

Использование выражений Break, Continue и Pass при работе с циклами в Python 3

Использование циклов for и циклов while в Python помогает эффективно автоматизировать и воспроизводить задачи.

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

Выражение Break

В Python выражение break дает вам возможность выйти из цикла при активации внешнего условия. Выражение break помещается в блок кода внутри выражения loop, обычно после условного выражения if .

Рассмотрим пример использования выражения break в цикле for :

number = 0 for number in range(10): if number == 5: break # break here print('Number is ' + str(number)) print('Out of loop') 

В этой небольшой программе переменная number инициализируется как 0. Затем выражение for строит цикл, пока значение переменной number составляет меньше 10.

В цикле for имеется выражение if , которое задает условие, что если значение переменной number равно целому числу 5, то цикл прекращается.

В цикле также содержится выражение print() , которое выполняется с каждой итерацией цикла for , пока цикл не прекращается, поскольку оно располагается после выражения break .

Чтобы узнавать о выходе из цикла, мы добавили завершающее выражение print() вне цикла for .

При выполнении этого кода результат будет выглядеть следующим образом:

Output
Number is 0 Number is 1 Number is 2 Number is 3 Number is 4 Out of loop

Это показывает, что когда переменная number оценивается как эквивалентная целому числу 5, цикл прекращается, поскольку программа получает соответствующее указание через выражение break .

Выражение break заставляет программу выйти из цикла.

Выражение Continue

Выражение continue дает возможность пропустить часть цикла, где активируется внешнее условие, но при этом выполнить остальную часть цикла. При этом прерывается текущая итерация цикла, но программа возвращается к началу цикла.

Выражение continue размещается в блоке кода под выражением цикла, обычно после условного выражения if .

Мы используем ту же программу с циклом for , что и в разделе «Выражение Break» выше, но при этом используем выражение continue вместо выражения break :

number = 0 for number in range(10): if number == 5: continue # continue here print('Number is ' + str(number)) print('Out of loop') 

Отличие выражения continue от выражения break заключается в том, что код продолжит выполняться несмотря на прерывание, если значение переменной number будет оценено как равное 5. Давайте посмотрим на результаты:

Output
Number is 0 Number is 1 Number is 2 Number is 3 Number is 4 Number is 6 Number is 7 Number is 8 Number is 9 Out of loop

В этом выводе условие Number is 5 никогда не выполняется, но цикл продолжается после этого, чтобы выводить линии для чисел 6–10 до выхода из цикла.

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

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

Выражение Pass

При активации внешнего условия выражение pass позволяет обрабатывать условия без влияния на цикл; чтение кода будет продолжаться до появления выражения break или другого выражения.

Как и в случае с другими выражениями, выражение pass будет содержаться в блоке кода до выражения loop, обычно после условного выражения if .

Используя тот же код выше, попробуйте заменить выражение break или continue выражением pass :

number = 0 for number in range(10): if number == 5: pass # pass here print('Number is ' + str(number)) print('Out of loop') 

Выражение pass , появляющееся после условного выражения if , указывает программе на необходимость продолжить выполнение цикла и игнорировать тот факт, что переменная number оценивается как равная 5 во время одной из итераций.

Мы запустим программу и оценим вывод:

Output
Number is 0 Number is 1 Number is 2 Number is 3 Number is 4 Number is 5 Number is 6 Number is 7 Number is 8 Number is 9 Out of loop

Используя выражение pass в этой программе, мы видим, что программа работает точно так же, как если бы в ней не было условного выражения. Выражение pass предписывает программе игнорировать это условие и продолжать обычное выполнение программы.

Выражение pass может создавать минимальные классы или выступать в качестве замещающего элемента при работе с новым кодом и действовать на уровне алгоритмов, прежде чем отрабатывать детали.

Заключение

Выражения break , continue и pass в Python позволяют использовать циклы for и while в вашем коде более эффективно.

Чтобы больше поработать с выражениями break и pass , вы можете выполнить учебный модуль нашего проекта «Создание бота Twitterbot с помощью Python 3 и библиотеки Tweepy».

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *