Gavin M. Roy
Gavin M. Roy

Reputation: 4691

Overriding python threading.Thread.run()

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?

Upvotes: 49

Views: 79446

Answers (8)

Jerub
Jerub

Reputation: 42638

You really don't need to subclass Thread. 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()

Upvotes: 83

Anshu Gupta
Anshu Gupta

Reputation: 21

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()

Upvotes: 2

pmg7670
pmg7670

Reputation: 141

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')

Upvotes: 2

Gavin M. Roy
Gavin M. Roy

Reputation: 4691

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()

Upvotes: 13

pjkundert
pjkundert

Reputation: 489

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."

Upvotes: 10

Andreas Maier
Andreas Maier

Reputation: 3080

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

Upvotes: 4

ihm
ihm

Reputation: 2889

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!

Upvotes: 5

Vasil
Vasil

Reputation: 38136

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.

Upvotes: 0

Related Questions