Search code examples
pythonoopgarbage-collection

How to delete an object which contains a list of its bound methods


At this code, an object of Foo() class is still alive after creating new one. I guess that reason is in the circular references on appending object's list property. So, how to let garbage collector free old object, withot manual calling gc.collect()?

import gc

class Foo():
    def __init__(self):
        self.functions = []
        print('CREATE', self)

    def some_func(self):
        for i in range(3):
            self.functions.append(self.print_func)
        print(self.functions)

    def print_func(self):
        print('I\'m a test')

    def __del__(self):
        print('DELETE', self)


foo = Foo()
foo.some_func()
foo = Foo()

# gc.collect()

input = input()

input() at the end is just for keeping program running. Real project with this problem contains while loop, so keeping old unused objects may cause a memory leak. Now, output:

CREATE <__main__.Foo object at 0x000002747001D850>
[<bound method Foo.print_func of <__main__.Foo object at 0x000002747001D850>>, <bound method Foo.print_func of <__main__.Foo object at 0x000002747001D850>>, <bound method Foo.print_func of <__main__.Foo object at 0x000002747001D850>>]
CREATE <__main__.Foo object at 0x000002746FD0BB90>

Output with calling gc.collect():

CREATE <__main__.Foo object at 0x0000021E45F0D8E0>
[<bound method Foo.print_func of <__main__.Foo object at 0x0000021E45F0D8E0>>, <bound method Foo.print_func of <__main__.Foo object at 0x0000021E45F0D8E0>>, <bound method Foo.print_func of <__main__.Foo object at 0x0000021E45F0D8E0>>]
CREATE <__main__.Foo object at 0x0000021E45CDBB90>
DELETE <__main__.Foo object at 0x0000021E45F0D8E0>

It's a result, that I want to get, but without using gc


Solution

  • You can use weakref.WeakMethod to avoid creating strong references to the self.print_func method in the list attribute.

    Note that to call a weakly referenced method you would have to dereference it first by calling the weakref before calling the actual method:

    from weakref import WeakMethod
    
    class Foo():
        def __init__(self):
            self.functions = []
            print('CREATE', self)
    
        def some_func(self):
            for i in range(3):
                self.functions.append(WeakMethod(self.print_func))
            print(self.functions)
    
        def print_func(self):
            print('I\'m a test')
    
        def __del__(self):
            print('DELETE', self)
    
    
    foo = Foo()
    foo.some_func()
    foo.functions[0]()()
    foo = Foo()
    input()
    

    This outputs:

    CREATE <__main__.Foo object at 0x0000018F0B397150>
    [<weakref at 0x0000018F0B18E0A0; to 'Foo' at 0x0000018F0B397150>, <weakref at 0x0000018F0B18E1F0; to 'Foo' at 0x0000018F0B397150>, <weakref at 0x0000018F0B18E490; to 'Foo' at 0x0000018F0B397150>]
    I'm a test
    CREATE <__main__.Foo object at 0x0000018F0B397190>
    DELETE <__main__.Foo object at 0x0000018F0B397150>
    

    Demo: https://replit.com/@blhsing1/LightheartedTurquoiseChord