Python: есть ли способ остановить бесконечный цикл, выполняемый в методе класса Python?

Я запускаю цикл для метода класса Python, который зависит от логической переменной. Когда я пытаюсь изменить значение переменной извне, кажется, что она застряла в цикле, и я не могу ни изменить, ни запустить следующие строки.

import time
class Test:
    def __init__(self):
        self.counter = 0
        self.boolean = True
    
    def test1(self):
        while self.boolean:
            time.sleep(0.5)
            print(self.counter)
            self.counter += 1


class Test2:
    def __init__(self):
        self.test = Test()
        self.test.test1()
        
    def stop_test(self):
        time.sleep(2)
        print("Now I change the boolean value")
        self.test.boolean = False

test = Test2()
print("What is going on?")
test.stop_test()

Этот код суммирует мою проблему.

Я ожидал, что цикл завершится, но вместо этого цикл продолжает работать, а код печатает только значение self.counter.

Единственный поток занят выполнением цикла и больше ничего делать не может. Вам нужна вторая нить. См. модуль threading в стандартной библиотеке.

Michael Butscher 10.04.2024 13:35

Этот код является синхронным, что означает, что ему необходимо завершить выполнение self.test.test1(), а затем он перейдет к следующей команде. В вашем примере он запустит цикл и не выйдет из функции до тех пор, пока цикл не завершится. Пока этот цикл зацикливается, никакой другой код не может быть выполнен (по крайней мере, так, как вы пытаетесь сделать). Если вы хотите, чтобы две команды работали одновременно, проверьте потоковую обработку.

Milos Stojanovic 10.04.2024 13:37

Вы не можете изменить значение снаружи, потому что вы никогда не выходите наружу. Запустите его в консоли и нажмите Control-C, чтобы увидеть, где находится ваш код. Как видите, вы будете на Test2.__init__, который вызывает test1. PS: научитесь использовать лучшие имена переменных, в том числе для тестов и примеров: это поможет вам вместо того, чтобы запоминать, что такое тест (а это не тест: разные классы), test1, test2 - не волнуйтесь, если вам нужно потратить несколько минут думать об этом.

Giacomo Catenazzi 10.04.2024 13:38

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

Joachim Huet 10.04.2024 13:39
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
100
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы можете добиться этого с помощью потоков, что-то вроде:

import time
import threading

class Test:
    def __init__(self):
        self.counter = 0
        self.boolean = True
    
    def test1(self):
        while self.boolean:
            time.sleep(0.5)
            print(self.counter)
            self.counter += 1

class Test2:
    def __init__(self):
        self.test = Test()
        self.thread = threading.Thread(target=self.test.test1)  # Run test1 in a separate thread
        self.thread.start()  # Start the thread
        
    def stop_test(self):
        time.sleep(2)
        print("Now I change the boolean value")
        self.test.boolean = False

test = Test2()
print("What is going on?")
test.stop_test()

Взгляните на этот модифицированный пример: обратите внимание, что теперь ваш класс Test основан на классе Thear, который реализует метод Thread.run(). Метод run() вызывается классом Thread при вызове start().

import time
from threading import Thread


class Test(Thread):

    def __init__(self):
        super().__init__()
        self.counter = 0
        self.boolean = True

    def run(self):
        while self.boolean:
            time.sleep(0.5)
            print(self.counter)
            self.counter += 1


class Test2:
    def __init__(self):
        self.test = Test()
        self.test.start()

    def stop_test(self):
        time.sleep(2)
        print("Now I change the boolean value")
        self.test.boolean = False


if __name__ == "__main__":
    test = Test2()
    print("What is going on?")
    test.stop_test()

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

Лучшей практикой (ИМХО) является использование событий, а не простых логических типов, поскольку событие предлагает дополнительные функции, такие как ожидание().

from threading import Thread, Event
from time import sleep

class Foo(Thread):
    def __init__(self):
        super().__init__()
        self._event = Event()
    @property
    def event(self):
        return self._event
    def run(self): # this is called during Thread.start()
        # wait for the Event to be set
        self.event.wait()
        # for as long as the Event is set
        while self.event.is_set():
            print("Sleeping")
            sleep(1)
        # Event has been cleared
        print("Stopping")
    def stop(self):
        self.event.clear()
    def start(self):
        super().start()
        self.event.set()

class Bar():
    def __init__(self):
        self._foo = Foo()
        self._foo.start()
    def __enter__(self):
        return self
    def __exit__(self, *_):
        self._foo.stop()
        self._foo.join()


with Bar():
    sleep(5)

Выход:

Sleeping
Sleeping
Sleeping
Sleeping
Sleeping
Stopping

Другие вопросы по теме

Почему я не могу условно расширить пользовательский класс элемента HTML?
Вызов функции класса только один раз при изменении переменной
RangeError: превышен максимальный размер стека вызовов – Typescript
Почему редактирование значения с использованием указателя, то есть частного поля класса, требует объявления дополнительной переменной?
Как устранить ошибку компилятора: в моем случае нет соответствующей функции для вызова dmhFS::dmhFS()?
Getter возвращает неопределенное значение в операторе if. Кажется, не работает для сравнения
Как сохранить/получить информацию о классе в/из таблицы?
C++: Как реализовать указатель на другой класс в качестве члена?
Как сделать так, чтобы я мог передать указатель класса на другой класс, который был объявлен из первого класса
Является ли использование классов и частных полей в качестве альтернативы переменным среды плохой идеей?