从main()访问Python类实例属性失败



我正在编写一个控制程序,它需要在自己的线程中运行多个可配置、可重启的定时器。最初,我使用自定义函数和所有计时器的自定义事件名称来实现这一点。因为这是一种非常丑陋的方法,所以我正在用一种更像OO的方法进行重写。请考虑下面的最小示例,它完成了我需要它做的一切,但有一个例外,我一直无法找到解决方案:我无法访问所需的类实例属性(我希望我在这里使用的术语是正确的(。

失败的语句是第74行,并被注释掉。我怀疑我传递论点和夸格的方式有问题,但遇到了砖墙。你能看到我哪里出了问题并提出解决方案吗?

#!/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('bbr')
print("PROGRAM TERMINATED AT USER'S REQUEST")
force_exit()

首先,您尝试初始化基类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))

但是intervalMyTimer实例的属性,而不是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('bbr')
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