Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 Confusion with sublcassing a threading class, and inheritance
#1
I am trying to understand how to subclass a Thread,
and I am confused with some details of inheritance.
It seems if I want to modify
the __init__ I need to call super as follows:





class MyThread(threading.Thread):
    def __init__(self, url, browser, *args, **kwargs):
        super(MyThread, self).__init__(*args, **kwargs)
        self.url = url
        self.browser = browser


This allows me to inherit all of the parents init
attributes as I am using *args, **kwargs and I can call MyThread._target within the initializer and it works.
However it seems I don't need to call super to
modify the run method. I have seen this example online:


class MyThread(threading.Thread):
 
    def __init__(self, number, logger):
        threading.Thread.__init__(self)
        self.number = number
        self.logger = logger
 
    def run(self):
        """
        Run the thread
        """
        #modification to run method but no call to it
        logger.debug('Calling doubler')
        doubler(self.number, self.logger)


Here it seems they are overwriting the parent init with The threading.Thread.__init__(self) ?
However, the threading.Thread.__init__(self) is not
calling any parameters, so it's essentially an empty __init__ and
doesn't acquire any of the parents attributes such as target, args, group.
So it seems as if they are creating a new init. So why even call threading.Thread.__init__ if you are not going to inherit any
attributes?

And why doesn't run require threading.Thread.run() if they are modifying the run method?
It seems only init is requiring the call to the original init



Now I was trying to access the ._target after super inheritance, but the attribute isn't found in the run method:


class MyThread(threading.Thread):
    def __init__(self, number, style, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.number = number
        self.style = style
        print(self._target) # works
    
    def run(self, *args, **kwargs):
        super().run(*args, **kwargs)
        print(self._target) # leads to error
        print('thread has ended')


   


custom = MyThread(target = print, number = 3, style ="red", args = ("test",))
 custom.run()
Error:
<built-in function print> test Traceback: custom.run() print(self._target) AttributeError: 'MyThread' object has no attribute '_target'[/python]
So these are the things I am confused about and am hoping someone can clarify these details.

Thank you.
Quote
#2
By doing
class MyThread(threading.Thread):
  
    def __init__(self, number, logger):
your init is overwriting the original init
both super(MyThread, self).__init__(*args, **kwargs) and threading.Thread.__init__(self)
call the original init.
it is sometime necessary to call an init with no given arguments because the init does other wanted things.

The following is threading.Thread init
def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
        """This constructor should always be called with keyword arguments. Arguments are:

        *group* should be None; reserved for future extension when a ThreadGroup
        class is implemented.

        *target* is the callable object to be invoked by the run()
        method. Defaults to None, meaning nothing is called.

        *name* is the thread name. By default, a unique name is constructed of
        the form "Thread-N" where N is a small decimal number.

        *args* is the argument tuple for the target invocation. Defaults to ().

        *kwargs* is a dictionary of keyword arguments for the target
        invocation. Defaults to {}.

        If a subclass overrides the constructor, it must make sure to invoke
        the base class constructor (Thread.__init__()) before doing anything
        else to the thread.

        """
        assert group is None, "group argument must be None for now"
        if kwargs is None:
            kwargs = {}
        self._target = target
        self._name = str(name or _newname())
        self._args = args
        self._kwargs = kwargs
        if daemon is not None:
            self._daemonic = daemon
        else:
            self._daemonic = current_thread().daemon
        self._ident = None
        self._tstate_lock = None
        self._started = Event()
        self._is_stopped = False
        self._initialized = True
        # sys.stderr is not stored in the class like
        # sys.exc_info since it can be changed between instances
        self._stderr = _sys.stderr
        # For debugging and _after_fork()
        _dangling.add(self)
with the run method you only call its super if you want the original run to work, the reason you cant call self._target
is because you are calling the original run method which if you take a look at it theres a del self._target, self._args, self._kwargs at the end.
 def run(self):
        """Method representing the thread's activity.

        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.

        """
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._target, self._args, self._kwargs
you don't want this behaviour so don't call super on the original run method
Quote
#3
You were almost right.
The super call in the init method is right, the super call in the run method is wrong.
You are overriding the run method. The original run calls the function _target.
In your case this attribute is empty, because you have not set it.
You're going the way to overload the run method with your own code.

The run method should never called directy. The start method of the original class Thread does this.
This is the funny part. The original start method calls the overridden run method of the child class.

class MyThread(threading.Thread):
    def __init__(self, number, style, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.number = number
        self.style = style
        print(self._target) # works
        # Your target is empty, the run method is called by start
     
    def run(self, *args, **kwargs):
        """
        This method should not called directly.
        Use the start() method.
        """
        # target is still empty, because it's not set
        print(self._target) # leads to error
        print('thread has ended')
Output:
In [19]: MyThread(1,2).start() None None thread has ended In [20]:
My code examples are always for Python >=3.6.0
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Quote

Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  threading for method outside class anna 0 123 Nov-17-2019, 07:05 AM
Last Post: anna
  Multiple Inheritance - Help pls! magnusbrigido 1 355 May-17-2019, 12:56 PM
Last Post: ichabod801
  Multiple inheritance - the right way ? denis_beurive 6 907 Feb-14-2019, 09:24 AM
Last Post: denis_beurive
  Inheritance Athul 7 990 Aug-11-2018, 06:48 PM
Last Post: yksingh1097
  Using Dictionaries in inheritance feduser 2 818 Jun-21-2018, 08:31 PM
Last Post: volcano63
  Threading : class instance to be executed by a thread user2103 0 1,563 Jan-17-2018, 01:08 PM
Last Post: user2103
  inheritance Arifattal 3 1,182 Jan-15-2018, 02:52 PM
Last Post: Arifattal
  Class Attributes Inheritance Harry_Potter 3 1,289 Nov-16-2017, 07:01 PM
Last Post: snippsat

Forum Jump:


Users browsing this thread: 1 Guest(s)