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? Continue reading...