Preferred way of resetting a class in Python

Question:

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?

Asked By: JAD

||

Answers:

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.

Answered By: Right leg

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.

Answered By: Serge Ballesta

As others said, you don’t need to reset a class. But if you really want it, you can use a factory function that returns a new class for every call.

def make_Foo():
    class Foo():
        VALUE = 1
    return Foo

Foo = make_Foo()
Foo.VALUE = 2
print(Foo.VALUE)  # prints 2

Foo = make_Foo()  # reset
print(Foo.VALUE)  # prints 1
Answered By: proski

I had a similar question myself and had discovered that the best way to do it is:

class MyClass:

    def __init__(self):
        self.reset()

    def reset(self):
        self.my_attribute_1 = 0
        self.my_attribute_2 = None
        self.my_attribute_3 = False

Now you have a reset() method which you can call on an instance of MyClass whenever you want to reset all those attributes.

EDIT: However, if you have a dataclass, you need to do it like this:

from dataclasses import dataclass

@dataclass
class MyClass:

    def __post_init__(self):
        self.reset()

    def reset(self):
        self.my_attribute_1 = 0
        self.my_attribute_2 = None
        self.my_attribute_3 = False
Answered By: BoĊĦtjan Mejak
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.