Я запускаю цикл для метода класса 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.
Этот код является синхронным, что означает, что ему необходимо завершить выполнение self.test.test1()
, а затем он перейдет к следующей команде. В вашем примере он запустит цикл и не выйдет из функции до тех пор, пока цикл не завершится. Пока этот цикл зацикливается, никакой другой код не может быть выполнен (по крайней мере, так, как вы пытаетесь сделать). Если вы хотите, чтобы две команды работали одновременно, проверьте потоковую обработку.
Вы не можете изменить значение снаружи, потому что вы никогда не выходите наружу. Запустите его в консоли и нажмите Control-C, чтобы увидеть, где находится ваш код. Как видите, вы будете на Test2.__init__
, который вызывает test1
. PS: научитесь использовать лучшие имена переменных, в том числе для тестов и примеров: это поможет вам вместо того, чтобы запоминать, что такое тест (а это не тест: разные классы), test1, test2 - не волнуйтесь, если вам нужно потратить несколько минут думать об этом.
Да, в такой однопоточной программе вам нужно поместить условие выхода из цикла в самом цикле.
Вы можете добиться этого с помощью потоков, что-то вроде:
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
Единственный поток занят выполнением цикла и больше ничего делать не может. Вам нужна вторая нить. См. модуль
threading
в стандартной библиотеке.