0

I have a singleton class and I do not understand how the Python garbage collector is not removing the instance.

I'm using - from singleton_decorator import singleton

example of my class:

from singleton_decorator import singleton

@singleton
class FilesRetriever:

    def __init__(self, testing_mode: bool = False):
        self.testing_mode = testing_mode

test example:

def test_singletone(self):
    FilesRetriever(testing_mode=True)
    mode = FilesRetriever().testing_mode
    print("mode 1:" + str(mode))
    mode = FilesRetriever().testing_mode
    print("mode 2:" + str(mode))
    count_before = gc.get_count()
    gc.collect()
    count_after = gc.get_count()
    mode = FilesRetriever().testing_mode
    print("mode 3:" + str(mode))
    print("count_before:" + str(count_before))
    print("count_after:" + str(count_after))   

test output:

mode 1:True
mode 2:True
mode 3:True
count_before:(306, 10, 5)
count_after:(0, 0, 0)

I would expect after the garbage collector runs automatically or after I ran it in my test that the instance of _SingletonWrapper (the class in the decorator implementation) will be removed because nothing is pointing to it. and then the value of "print("mode 3:" + str(mode))" will be False because that is the default (and the instance was re-created)

MLavoie
  • 9,671
  • 41
  • 36
  • 56
  • Does this answer your question? [When is a singleton class object garbage collected?](https://stackoverflow.com/questions/32359681/when-is-a-singleton-class-object-garbage-collected) – Mark Carpenter Jr Aug 10 '20 at 22:45

1 Answers1

0

So the code and garbage collection is working as intended. Look at the code for the singleton decorator you are referring to. decorator

Just because you call gc.collect() and your code isn't holding a reference somewhere doesn't mean other code isn't.

In the decorator it creates an instance then stores that instance in a variable within the decorator. So even though you collected relative to your code. Their code is still holding a reference to that instance and so it doesn't get collected.

This would be expected behavior from a singleton since that is its whole purpose. Is to store an instance somewhere that can be retrieved and used instead of creating a new instance every time. So you wouldn't want that instance to be trashed unless you need to replace the instance.

To answer your comment

  1. No, you are not getting the instance to the _SingletonWrapper. When you write FileRetriever() what you're actually doing is invoking the __call__ method of the instance of _SingletonWrapper. When you use @singleton() that returns an instance not the class object.

  2. Again while in your code you are not storing it anywhere doesn't mean it isn't stored some where else. When you define a class what you are doing in a sense is in the global scope of the module it is creating a variable that holds that class definition. So in you code in the global scope it has something like this,

FileRetriever = (class:
    def __init__(self):
        blahblahblah

So now your class definition is stored in a variable call FileRetriever.

So now you're using a decorator, so now it looks like this based on the code in the single decorator.

FileRetriever = _SingletonWrapper(class: blahblah)

Now you're class is wrapped and stored in the variable FileRetriever. Now you invoke the _SingletonWrapper.__call__() when you run FileRetriever(). Because __call__ is an instance method. It can hold a reference to you're original class and instance of the class you declared and so even if you remove all you're references to that class this code is still holding that reference.

If you truly want to remove all references to you're singleton which I'm not sure why you would want to. You need to remove all references to the wrapper as well as you're class. So something like FileRetriever = None might cause the gc to collect it. But you would lose you're original class definition in the process.

TeddyBearSuicide
  • 1,377
  • 1
  • 6
  • 11
  • thank you, the decorator returns the class _SingletonWrapper, I agree that in that class the instance for my class is held, but in my test code I get the _SingletonWrapper instance and I am not storing it in any variable, so should the gc.collect() remove it and then also the instance of my lass which it saved in self._instance ? – Eyal Gruber Aug 10 '20 at 15:25
  • I edited my answer since there's a lot of explaining. Please see my edit. – TeddyBearSuicide Aug 10 '20 at 19:00