Python's threading module has two interfaces. In one you instantiate threading.Thread and pass to it the function you want to run, schematically:
class MyClass:
def __init__(self):
self.my_vars = {}
def my_func(self):
# does stuff
self.my_vars = # set result of running the function here
mc = MyClass()
t = threading.Thread(target=mc.my_func) # also, pass arguments
# set some options to t, like t.setDaemon(True)
t.start()
In the other you subclass threading.Thread and subclass the run method, schematically:
class MyClass(threading.Thread):
def __init__(self):
super(MyClass,self).__init__(*args, **kwargs)
self.my_vars = {}
def run(self):
# does stuff
self.my_vars = # set result of running the function here
t = MyThreadedClass()
t.start()
I started out using the first one, but at some point realized that I was having to write a lot of boilerplate every time I wanted to start a thread to run my_func: I kept having to remind myself the syntax to pass argments to my_func, plus I had to write multiple lines to set thread options etc etc. So I decided to move to the second style. In this way I just instantiate my class and then call .start(). Note that at this stage the difference is only in how easy it is to use these things, as my_func and run are exactly the same.
But now I'm realizing that this made my code harder to test. Before if I wanted to test my_func under so and so arguments, I just had to import the file where it is defined and then run it on some input. Or I could even do it from a jupyter notebook and play it its inputs to see its outputs. But with the second style, every time I want to do something as simple as run my_func, it comes with a thread attached.
Is there a way I can organize my code that it's cleaner for an application to run it on its own thread, but that has no thread when I want to call it for example from a notebook?