1

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?

1 Answer 1

3

Make objects of your class callable:

from abc import ABCMeta, abstractmethod
import threading

class Callable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __call__(self): raise NotImplementedError

class MyCallable(Callable):
    def __init__(self, x):
        self.x = x

    def __call__(self):
        print('x=', self.x)


# without a thread:
callable = MyCallable(7)
callable()

# on a thread:
callableThread = threading.Thread(target=callable)
callableThread.start()
callableThread.join()

You can do without the formality of using abstract base classes and abstract methods -- just make sure your class defines a __call__ method that does the work you need to do currently being done by your my_func.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for responding, please see the edit at the bottom
There isn't any one correct answer, so people will have different opinions -- they are called solutions. I think it was hasty to have closed this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.