从 main() 访问 Python class 实例属性失败
Access to Python class instance attributes from main() fails
我正在编写一个控制程序,它需要多个可配置、可重启的计时器,这些计时器 运行 在它们自己的线程中。最初我用所有计时器的自定义函数和自定义事件名称实现了这一点。因为这是一种非常丑陋的方法,所以我正在使用更类似于 OO 的方法进行重写。请考虑下面的最小示例,它完成了我需要它做的所有事情,但有一个例外,我无法找到解决方案:我无法访问所需的 class 实例属性(我希望我是在这里使用正确的术语)。
失败语句在第 74 行,已被注释掉。我怀疑我传递参数和 kwargs 的方式有问题,但 运行 进入了砖墙。你能看出我哪里出了问题并提出解决方案吗?
#!/usr/bin/python3
# coding: utf-8
"""MRE; needs installation of sine.threads ReStartableThread: pip3 install sine.threads"""
import sys
import time
import threading
from sine.threads import ReStartableThread as Thread
class MyThread(Thread):
"""
drop-in replacement of ReStartableThread with status method added
"""
def __init__(self, name=None, event_name=None, target=None, group=None, args=(), kwargs=None):
Thread.__init__(self)
self.name = name
self.event_name = event_name
self.target = target
self.args = args
self.kwargs = kwargs
super().__init__(name=name, event_name=event_name, target=target, group=group, args=args, kwargs=kwargs)
def status(self, i): # placeholder
print('dummy status #', i)
def threader(f):
""" a threading decorator; put @threaded above the callable that needs threading """
def threading_function(*args, **kwargs):
threading.Thread(name=f.__name__, target=f, args=args, kwargs=kwargs).start()
return threading_function
@threader
def t0():
time.sleep(3)
t1.stop()
t1.join()
class MyTimer:
def __init__(self, name, kwargs):
for key, value in kwargs.items(): # unpack kwargs and convert to variables
setattr(self, key, value)
def __call__(self, stop_event):
i = 5
while not stop_event.is_set() and i > 0:
print('I: ', i)
time.sleep(1)
i -= 1
if stop_event.is_set():
print('timer is CANCELLED')
else:
print('timer is FINISHED')
def main():
global t1
name = 'timer'
t1_kwargs = {'interval': 99, 'message': 'STARTING COUNTDOWN...'}
t1 = MyThread(name=name, target=MyTimer(name, t1_kwargs))
t1.status(1)
# first countdown
t1.start()
"""
The next statement fails with "AttributeError: 'StoppableThread' object has no attribute 'interval'"
where StoppableThread is a member of the sine.threads module.
Requirement: access to attributes (like interval) of instances of MyTimer from main
"""
# print(t1.interval)
t0()
t1.join()
t1.status(2)
# second countdown
t1.start()
t1.status(3)
t1.join()
t1.status(4)
print('END')
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\b\b\r')
print("PROGRAM TERMINATED AT USER'S REQUEST")
force_exit()
首先,您正在尝试初始化您的基础 class Thread
两次,首先是
Thread.__init__(self)
然后是:
super().__init__(name=name, event_name=event_name, target=target, group=group, args=args, kwargs=kwargs)
你应该摆脱第一次初始化。
您的问题是您正在将 t1
分配给您的 MyThread
实例:
t1 = MyThread(name=name, target=MyTimer(name, t1_kwargs))
但是 interval
是您的 MyTimer
实例的属性,而不是您的 MyThread
实例的属性。所以 t1.interval
会失败。你想要:
t1 = MyTimer(name, t1_kwargs)
my_thread = MyThread(name, target=t1)
my_thread.status(1)
my_thread.start()
etc.
你只需要保持你的两个不同实例:
#!/usr/bin/python3
# coding: utf-8
"""MRE; needs installation of sine.threads ReStartableThread: pip3 install sine.threads"""
import sys
import time
import threading
from sine.threads import ReStartableThread as Thread
class MyThread(Thread):
"""
drop-in replacement of ReStartableThread with status method added
"""
def __init__(self, name=None, event_name=None, target=None, group=None, args=(), kwargs=None):
self.name = name
self.event_name = event_name
self.target = target
self.args = args
self.kwargs = kwargs
super().__init__(name=name, event_name=event_name, target=target, group=group, args=args, kwargs=kwargs)
def status(self, i): # placeholder
print('dummy status #', i)
def threader(f):
""" a threading decorator; put @threaded above the callable that needs threading """
def threading_function(*args, **kwargs):
threading.Thread(name=f.__name__, target=f, args=args, kwargs=kwargs).start()
return threading_function
@threader
def t0():
time.sleep(3)
my_thread.stop()
my_thread.join()
class MyTimer:
def __init__(self, name, kwargs):
for key, value in kwargs.items(): # unpack kwargs and convert to variables
setattr(self, key, value)
def __call__(self, stop_event):
i = 5
while not stop_event.is_set() and i > 0:
print('I: ', i)
time.sleep(1)
i -= 1
if stop_event.is_set():
print('timer is CANCELLED')
else:
print('timer is FINISHED')
def main():
global t1
global my_thread
name = 'timer'
t1_kwargs = {'interval': 99, 'message': 'STARTING COUNTDOWN...'}
t1 = MyTimer(name, t1_kwargs)
my_thread = MyThread(name, target=t1)
my_thread.status(1)
my_thread.start()
print(t1.interval)
t0()
my_thread.join()
my_thread.status(2)
# second countdown
my_thread.start()
my_thread.status(3)
my_thread.join()
my_thread.status(4)
print('END')
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\b\b\r')
print("PROGRAM TERMINATED AT USER'S REQUEST")
force_exit()
打印:
dummy status # 1
I: 5
99
I: 4
I: 3
timer is CANCELLED
dummy status # 2
I: 5
dummy status # 3
I: 4
I: 3
I: 2
I: 1
timer is FINISHED
dummy status # 4
END
我正在编写一个控制程序,它需要多个可配置、可重启的计时器,这些计时器 运行 在它们自己的线程中。最初我用所有计时器的自定义函数和自定义事件名称实现了这一点。因为这是一种非常丑陋的方法,所以我正在使用更类似于 OO 的方法进行重写。请考虑下面的最小示例,它完成了我需要它做的所有事情,但有一个例外,我无法找到解决方案:我无法访问所需的 class 实例属性(我希望我是在这里使用正确的术语)。
失败语句在第 74 行,已被注释掉。我怀疑我传递参数和 kwargs 的方式有问题,但 运行 进入了砖墙。你能看出我哪里出了问题并提出解决方案吗?
#!/usr/bin/python3
# coding: utf-8
"""MRE; needs installation of sine.threads ReStartableThread: pip3 install sine.threads"""
import sys
import time
import threading
from sine.threads import ReStartableThread as Thread
class MyThread(Thread):
"""
drop-in replacement of ReStartableThread with status method added
"""
def __init__(self, name=None, event_name=None, target=None, group=None, args=(), kwargs=None):
Thread.__init__(self)
self.name = name
self.event_name = event_name
self.target = target
self.args = args
self.kwargs = kwargs
super().__init__(name=name, event_name=event_name, target=target, group=group, args=args, kwargs=kwargs)
def status(self, i): # placeholder
print('dummy status #', i)
def threader(f):
""" a threading decorator; put @threaded above the callable that needs threading """
def threading_function(*args, **kwargs):
threading.Thread(name=f.__name__, target=f, args=args, kwargs=kwargs).start()
return threading_function
@threader
def t0():
time.sleep(3)
t1.stop()
t1.join()
class MyTimer:
def __init__(self, name, kwargs):
for key, value in kwargs.items(): # unpack kwargs and convert to variables
setattr(self, key, value)
def __call__(self, stop_event):
i = 5
while not stop_event.is_set() and i > 0:
print('I: ', i)
time.sleep(1)
i -= 1
if stop_event.is_set():
print('timer is CANCELLED')
else:
print('timer is FINISHED')
def main():
global t1
name = 'timer'
t1_kwargs = {'interval': 99, 'message': 'STARTING COUNTDOWN...'}
t1 = MyThread(name=name, target=MyTimer(name, t1_kwargs))
t1.status(1)
# first countdown
t1.start()
"""
The next statement fails with "AttributeError: 'StoppableThread' object has no attribute 'interval'"
where StoppableThread is a member of the sine.threads module.
Requirement: access to attributes (like interval) of instances of MyTimer from main
"""
# print(t1.interval)
t0()
t1.join()
t1.status(2)
# second countdown
t1.start()
t1.status(3)
t1.join()
t1.status(4)
print('END')
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\b\b\r')
print("PROGRAM TERMINATED AT USER'S REQUEST")
force_exit()
首先,您正在尝试初始化您的基础 class Thread
两次,首先是
Thread.__init__(self)
然后是:
super().__init__(name=name, event_name=event_name, target=target, group=group, args=args, kwargs=kwargs)
你应该摆脱第一次初始化。
您的问题是您正在将 t1
分配给您的 MyThread
实例:
t1 = MyThread(name=name, target=MyTimer(name, t1_kwargs))
但是 interval
是您的 MyTimer
实例的属性,而不是您的 MyThread
实例的属性。所以 t1.interval
会失败。你想要:
t1 = MyTimer(name, t1_kwargs)
my_thread = MyThread(name, target=t1)
my_thread.status(1)
my_thread.start()
etc.
你只需要保持你的两个不同实例:
#!/usr/bin/python3
# coding: utf-8
"""MRE; needs installation of sine.threads ReStartableThread: pip3 install sine.threads"""
import sys
import time
import threading
from sine.threads import ReStartableThread as Thread
class MyThread(Thread):
"""
drop-in replacement of ReStartableThread with status method added
"""
def __init__(self, name=None, event_name=None, target=None, group=None, args=(), kwargs=None):
self.name = name
self.event_name = event_name
self.target = target
self.args = args
self.kwargs = kwargs
super().__init__(name=name, event_name=event_name, target=target, group=group, args=args, kwargs=kwargs)
def status(self, i): # placeholder
print('dummy status #', i)
def threader(f):
""" a threading decorator; put @threaded above the callable that needs threading """
def threading_function(*args, **kwargs):
threading.Thread(name=f.__name__, target=f, args=args, kwargs=kwargs).start()
return threading_function
@threader
def t0():
time.sleep(3)
my_thread.stop()
my_thread.join()
class MyTimer:
def __init__(self, name, kwargs):
for key, value in kwargs.items(): # unpack kwargs and convert to variables
setattr(self, key, value)
def __call__(self, stop_event):
i = 5
while not stop_event.is_set() and i > 0:
print('I: ', i)
time.sleep(1)
i -= 1
if stop_event.is_set():
print('timer is CANCELLED')
else:
print('timer is FINISHED')
def main():
global t1
global my_thread
name = 'timer'
t1_kwargs = {'interval': 99, 'message': 'STARTING COUNTDOWN...'}
t1 = MyTimer(name, t1_kwargs)
my_thread = MyThread(name, target=t1)
my_thread.status(1)
my_thread.start()
print(t1.interval)
t0()
my_thread.join()
my_thread.status(2)
# second countdown
my_thread.start()
my_thread.status(3)
my_thread.join()
my_thread.status(4)
print('END')
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\b\b\r')
print("PROGRAM TERMINATED AT USER'S REQUEST")
force_exit()
打印:
dummy status # 1
I: 5
99
I: 4
I: 3
timer is CANCELLED
dummy status # 2
I: 5
dummy status # 3
I: 4
I: 3
I: 2
I: 1
timer is FINISHED
dummy status # 4
END