Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Opposite of __init__ in thread class?

I understand that __init__() is called automatically when you create a class like newThread = MyThread(property) and run() is triggered by newthread.start(). What I am looking for is something that is called automatically before a thread terminates, so I don't have to explicitly call self.cleanUp() before each return statement.

class MyThread(Thread): 
  
    def __init__(self, property): 
        Thread.__init__(self)
        self.property = property

    def cleanUp(self):
        # Clean up here
 
    def run(self):
        # Do some stuff
        self.cleanUp() # Current work around
        return
like image 340
Man Eating Monkey Avatar asked Apr 14 '17 14:04

Man Eating Monkey


People also ask

What is opposite to init in Python?

While __del__() is considered the opposite, there can be issues with when it is called.

What is INIT in thread?

__init__() method is called when an object is initialized. And when you do - Thread. __init__(self) , it just just calling the parent class' __init__() method . Like said in comment you can remove it and the functionality should remain same.

Can you define a class without init?

You can still instantiate a class that doesn't specify the __init__ method. Leaving it out does not make your class abstract. Please give context to your code. Answers containing code only without explanation and/or comments are not very useful.

What is __ init __ -> None?

def __init__(self, n) -> None: means that __init__ should always return NoneType and it can be quite helpful if you accidentally return something different from None especially if you use mypy or other similar things. But you can ignore it if you prefer the old way to do it. Follow this answer to receive notifications.


Video Answer


3 Answers

One way to do this is by making the Thread subclass also a context manager. This will effectively make __exit__() the special method you want triggered.

The following shows what I'm proposing. Note: I renamed the property argument you were passing the constructor because property is the name of a Python built-in.

from threading import Thread
import time

TEST_THREAD_EXCEPTION = False  # change as desired

class MyThread(Thread):

    def __init__(self, attribute):
        Thread.__init__(self)
        self.attribute = attribute

    def cleanup(self):
        # Clean up here
        print('  cleaning up after thread')

    def run(self):
        if TEST_THREAD_EXCEPTION:
            raise RuntimeError('OOPS!')  # force exception
        print('  other thread now running...')
        time.sleep(2)  # Do something...

    def __enter__(self):
        try:
            self.run()
        except Exception as exc:
            print('Error: {} exception raised by thread'.format(exc))
            raise  # reraise the exception
        return self

    def __exit__(self, *args):
        self.cleanup()

print('main thread begins execution')
with MyThread('hello') as thread:
    print('doing other things in main thread while other thread is running')
print('main thread continuing...')

Output:

main thread begins execution
  other thread now running...
doing other things in main thread while other thread is running
  cleaning up after thread
main thread continuing on...

If you change TEST_THREAD_EXCEPTION to True, cleanup() won't be called since the thread didn't run successfully—although you could change that if you wished, but may also need to ensure that it doesn't get called twice. Here's what the code above does in that case:

main thread begins execution
Error: OOPS! exception raised by thread
Traceback (most recent call last):
  File "opposite_init.py", line 37, in <module>
    with MyThread('hello') as thread:
  File "opposite_init.py", line 27, in __enter__
    self.run()
  File "opposite_init.py", line 21, in run
    raise RuntimeError('OOPS!')  # force exception
RuntimeError: OOPS!
like image 77
martineau Avatar answered Oct 21 '22 11:10

martineau


As stated in the Python mailing list, __del__ shouldn't be considered the opposite, but you can use the with syntax, which is a context manager

you cannot be sure that an object's destructor (__del__() ) will ever be called. If you want to make sure that a particular object gets processed, one approach is the with- syntax.

Or you can also look into the try...finally clause, in which the finally statement will always get run.

class MyThread(Thread): 

    def __init__(self, property): 
        Thread.__init__(self)
        self.property = property

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print('starting cleanup')
        # Clean up here

    def run(self):
        # Do some stuff
        return

# not now you can call it like this:
with MyThread("spam") as spam:
    print("The thread is running")
    # you can also do stuff here

You can use the try...finally clause like so:

class MyThread(Thread): 

    def __init__(self, property): 
        Thread.__init__(self)
        self.property = property

    def cleanUp(self):
        # Clean up here
        print('starting cleanup')

    def run(self):
        # Do some stuff
        return

try:
    spam = MyThread('spam')
    print('The thread is running')
finally:
    spam.cleanUp()
like image 25
Taku Avatar answered Oct 21 '22 12:10

Taku


If the problem you're trying to solve is that you don't want to add code to each of your run() methods to call your cleanup function, then I'd suggest making a custom subclass of Thread which does that for you. Something like this, perhaps:

class CleanupThread(Thread):
    def cleanup(self):
        # Override this method in your subclasses to do cleanup
        pass

    def run2(self):
        # Override this method in your subclasses instead of run()
        pass

    def run(self):
        # Do *not* override this in your subclasses. Override run2() instead.
        try:
            self.run2()
        finally:
            self.cleanup()

Of course, you're free to rename run2 to something that makes sense for you.

Python does not offer a built-in equivalent of this, if that's what you're looking for.

like image 37
Dolda2000 Avatar answered Oct 21 '22 11:10

Dolda2000