49

Given the Python documentation for Thread.run():

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

I have constructed the following code:

class DestinationThread(threading.Thread):
    def run(self, name, config):
        print 'In thread'

thread = DestinationThread(args = (destination_name, destination_config))
thread.start()

But when I execute it, I receive the following error:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py", line 522, in __bootstrap_inner
    self.run()
TypeError: run() takes exactly 3 arguments (1 given)

It seems I am missing something obvious, but the various examples I have seen work with this methodology. Ultimately I am trying to just pass the string and dictionary into the thread, if the Constructor is not the right way, but rather to make a new function to set the values prior to starting the thread, I am open to that.

Any suggestions on how to best accomplish this?

xlm
  • 6,854
  • 14
  • 53
  • 55
Gavin M. Roy
  • 4,551
  • 4
  • 33
  • 29
  • Readers like me may find [this](https://www.bogotobogo.com/python/Multithread/python_multithreading_subclassing_creating_threads.php) post directly answered PO's question, while the chosen answer gave a better practical alternative. – Zheng Liu Mar 29 '19 at 08:25

8 Answers8

84

You really don't need to subclass Thread. The only reason the API supports this is to make it more comfortable for people coming from Java where that's the only way to do it sanely.

The pattern that we recommend you use is to pass a method to the Thread constructor, and just call .start().

 def myfunc(arg1, arg2):
     print 'In thread'
     print 'args are', arg1, arg2

 thread = Thread(target=myfunc, args=(destination_name, destination_config))
 thread.start()
xlm
  • 6,854
  • 14
  • 53
  • 55
Jerub
  • 41,746
  • 15
  • 73
  • 90
  • 6
    This was a key piece of info I was missing, which is why I accepted it, but in my case, I actually am extending the class adding needed functions to pull data out of the thread. – Gavin M. Roy Mar 19 '09 at 03:36
  • 3
    A thousand times yes. I have to explain this one a lot to Java developers. – Ali Afshar Mar 19 '09 at 10:28
  • 1
    You don't even need args=(arg1,arg2). Just do target=functools.partial(myfunc, arg1, arg2). – Phob Jul 14 '11 at 21:41
  • 40
    Yes. I could avoid using the supplied API and instead import a different unnecessary one. That's true. – Jerub Jul 25 '11 at 19:59
  • 1
    @Jerub: `partial` could be adventageous over `args` because you can pass *args and **kwargs (or *any_list or **any_dict for that matter) whereas args expects a tuple. In this case the arguments are simple, but it might not always be the case – ihm Nov 13 '13 at 01:42
  • 3
    Yes, but, still this is not answering the actual question which was about subclassing. – Oleiade Jun 15 '16 at 14:55
13

Here's is an example of passing arguments using threading and not extending __init__:

import threading

class Example(threading.Thread):

    def run(self):
        print '%s from %s' % (self._Thread__kwargs['example'],
                              self.name)

example = Example(kwargs={'example': 'Hello World'})
example.start()
example.join()

And here's an example using mutliprocessing:

import multiprocessing

class Example(multiprocessing.Process):

    def run(self):
        print '%s from %s' % (self._kwargs['example'],
                              self.name)

example = Example(kwargs={'example': 'Hello World'})
example.start()
example.join()
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Gavin M. Roy
  • 4,551
  • 4
  • 33
  • 29
  • 3
    In Python 3, the attributes of Thread were changed from private to protected, i.e. `self.__kwargs` (within the Thread class) became `self._kwargs`. This allows for a slightly less ugly access, because the name mangling is no longer used. However, this change requires one to be Python version sensitive if one wants to support both Python 2 and 3. – Andreas Maier Apr 24 '15 at 14:18
  • 1
    One more: I don't see how one could implement an overridden `run()`method **without** directly accessing the Thread attributes `_target`, `_args`, and `_kwargs` (using their v3 names): Their values have to be used (see the description of run() that has been quoted in other responses), and they have no accessor methods. – Andreas Maier Apr 24 '15 at 14:24
  • Yet one more: The run() methods shown in this answer do not invoke a target method that someone would specify in the constructor, and thus is not compatible with the behavior documented for its base class. – Andreas Maier Apr 24 '15 at 14:34
  • what does this code actually do? Documentation says "The standard run() method invokes the callable object passed to the object’s constructor as the target argument" you dont do that, so your thread is only printing that line nothing more? – 463035818_is_not_an_ai Feb 01 '19 at 13:13
9

The documentation of threading.Thread may seem to imply that any unused positional and keyword args are passed to run. They are not.

Any extra positional args and keyword kwargs are indeed trapped by the default threading.Thread.__init__ method, but they are ONLY passed to a method specified using target= keyword. They are NOT passed to the run() method.

In fact, the Threading documentation at makes it clear that it is the default run() method that invokes the supplied target= method with the trapped args and kwargs:

"You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively."

xlm
  • 6,854
  • 14
  • 53
  • 55
pjkundert
  • 479
  • 4
  • 7
  • 1
    I agree. Some of the other answers to the original question get this wrong (those that show a `run()` method with arguments in addition to `self`). – Andreas Maier Apr 24 '15 at 14:26
  • This is actually the correct answer. The documentation is subtle and easy to misunderstand until you know this. It implies that subclasses should want to override `run()`. In reality, subclasses that want to pass arguments should set `target=self.myRun` in the call to `Thread.__init__`. Even reading the first few answers on this page, I struggled until I broke down and read threading.py – jwm Jul 18 '18 at 19:34
5

If you want to keep your object-oriented approach and also have run arguments, you can do the following:

import threading
class Destination:
    def run(self, name, config):
        print 'In thread'

destination = Destination()
thread = threading.Thread(target=destination.run,
    args=(destination_name, destination_config))
thread.start()

As mentioned above, it could also be done with partial

from functools import partial
import threading
class Destination:
    def run(self, name, config):
        print 'In thread'

destination = Destination()
thread = threading.Thread(target=partial(
    destination.run, destination_name, destination_config))
thread.start()

The advantage of doing this versus a purely-functional approach is that it lets you keep your other existing object-oriented code the same. The only change is to have it not subclass Thread, which shouldn't be a big deal, since per threading.Thread documentation:

only override the init() and run() methods of this class

If you were overriding Thread so that you could access the thread object from within your subclass, then I'd recommend just using threading.currentThread() from within your object. This way you segment the thread's namespace from your own, and per the "Zen of Python" by Tim Peters:

Namespaces are one honking great idea -- let's do more of those!

ihm
  • 2,789
  • 1
  • 18
  • 6
  • This answer was very helpful. However, `threading.currentThread()` has been deprecated in 3.10. Instead use `threading.current_thread()` (which also exists in Python 2.7). – Zim Aug 18 '22 at 21:55
3

In order to address some of the confusion about whether an overridden run() method takes additional arguments, here is an implementation of an overridden run() method that does what the method inherited from threading.Thread does.

Note, this just to see how one would override run(); it is not meant to be a meaningful example. If all you want to do is invoking a target function with sequential and/or keyword arguments, it is not necessary to have a subclass; this has been pointed out e.g. in Jerub's answer to this question.

The following code supports both Python v2 and v3.

Although particularly the access to the mangled attribute names in the Python 2 code is ugly, I am not aware of another way to access these attributes (let me know if you know one...):

import sys
import threading

class DestinationThread(threading.Thread):

    def run(self):
        if sys.version_info[0] == 2:
            self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
        else: # assuming v3
            self._target(*self._args, **self._kwargs)

def func(a, k):
    print("func(): a=%s, k=%s" % (a, k))

thread = DestinationThread(target=func, args=(1,), kwargs={"k": 2})
thread.start()
thread.join()

It prints (tested with Python 2.6, 2.7, and 3.4 on Windows 7):

func(): a=1, k=2
Community
  • 1
  • 1
Andreas Maier
  • 2,724
  • 1
  • 26
  • 30
2

Since Thread constructor argument target is a callable, apply __call__ as the run method

class Worker(object):
  def __call__(self, name, age):
    print('name, age : ',name,age)

if __name__ == '__main__':

  thread = Thread(target=Worker(), args=('bob','50'))
  thread.start()

output :

('name, age : ', 'bob', '50')
entpnerd
  • 10,049
  • 8
  • 47
  • 68
pmg7670
  • 101
  • 5
1

if you really need a subclass, you can use it like this

>>> class DestinationThread(threading.Thread):
    def __init__(self,name, config):
        super().__init__()
        self.name = name
        self.config = config
    def run(self):
        print("In thread")

you can access name and config by self.name & self.config

>>> thread = DestinationThread(destination_name, destination_config)
>>> thread.start()

it will give you the accepted output.

output:

In thread

or you can use, target parameter.

thread = Thread(target=some_func, args=[arg1, arg2])
thread.start()
0

You define the run method to accept 3 arguments, but you call it with one argument (python calls it with the reference to the object).

You need to pass the arguments to run instead of __init__.

Or make the __init__ method accept the arguments instead.

Vasil
  • 36,468
  • 26
  • 90
  • 114