Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preferred way of resetting a class in Python

Tags:

Based on this post on CodeReview.

I have a class Foo in Python (3), which of course includes a __init__() method. This class fires a couple of prompts and does its thing. Say I want to be able to reset Foo so I can start the procedure all over again.

What would be the preferred implementation?

Calling the __init__() method again

def reset(self):     self.__init__() 

or creating a new instance?

def reset(self):     Foo() 

I am not sure if creating a new instance of Foo leaves behind anything that might affect performance if reset is called many times. On the other hand __init__() might have side-effects if not all attributes are (re)defined in __init__().

Is there a preferred way to do this?

like image 479
JAD Avatar asked Aug 21 '17 13:08

JAD


2 Answers

Both are correct but the semantics are not implemented the same way.

To be able to reset an instance, I would write this (I prefere to call a custom method from __init__ than the opposite, because __init__ is a special method, but this is mainly a matter of taste):

class Foo:     def __init__(self):         self.reset()     def reset(self):         # set all members to their initial value 

You use it that way:

Foo foo      # create an instance ... foo.reset()  # reset it 

Creating a new instance from scratch is in fact simpler because the class has not to implement any special method:

Foo foo      # create an instance ... foo = Foo()  # make foo be a brand new Foo 

the old instance will be garbage collected if it is not used anywhere else

Both way can be used for normal classes where all initialization is done in __init__, but the second way is required for special classes that are customized at creation time with __new__, for example immutable classes.


But beware, this code:

def reset(self):     Foo() 

will not do what you want: it will just create a new instance, and immediately delete it because it will go out of scope at the end of the method. Even self = Foo() would only set the local reference which would the same go out of scope (and the new instance destroyed) at the end of the methos.

like image 162
Serge Ballesta Avatar answered Sep 18 '22 17:09

Serge Ballesta


You could hold the instance you work with in a class attribute. Whenever you want to reset the class, reassign a new instance to that attribute. Here is how I would implement this approach:

class Foo:     instance = None    # The single instance      def __init__(self, ...):         # Initialize the instance if Foo.instance does not exist, else fail         if type(self).instance is None:             # Initialization             type(self).instance = self         else:             raise RuntimeError("Only one instance of 'Foo' can exist at a time")      @classmethod     def reset(cls):         cls.instance = None        # First clear Foo.instance so that __init__ does not fail         cls.instance = Foo(...)    # Now the initialization can be called 

Then, you can access the instance by simply referring to Foo.instance.

I chose to have the reset as a class method, thus decorated by @classmethod. With this decorator, the instance can be reset by calling Foo.reset(), and the cls parameter will be passed automatically to the method.

I prefer this approach (which is more or less a singleton pattern) over those you suggest, because in your situation, it appears logical to have a single instance of Foo, since you want to reset it. Therefore, I find it rather intuitive to "force" the use of a single instance.

On the other hand, you could have an instance outside of the class, and use a reset instance method, defined:

def reset(self):     self.__init__() 

But this might not work so well. Say you want to set attributes outside of the __init__ method. Calling __init__ will not reset those attributes. Therefore, your instance will not be reset as expected. Now if you hold a single instance and reassign it to a brand new one, you're certain that it will be absolutely clean.

Regarding what you call "creating a new instance", that's more or less what I chose, but the question is where to store it. I think it makes sense to keep it warm in the class itself.

By the way, there shouldn't be any performance issue (as in "memory leak") with this solution, since only one Foo instance is referenced at a time, and creating a new one will de-reference the previous one.

like image 24
Right leg Avatar answered Sep 18 '22 17:09

Right leg