Printing all instances of a class

With a class in Python, how do I define a function to print every single instance of the class in a format defined in the function?


Solution 1:

I see two options in this case:

Garbage collector

import gc
for obj in gc.get_objects():
    if isinstance(obj, some_class):
        dome_something(obj)

This has the disadvantage of being very slow when you have a lot of objects, but works with types over which you have no control.

Use a mixin and weakrefs

from collections import defaultdict
import weakref

class KeepRefs(object):
    __refs__ = defaultdict(list)
    def __init__(self):
        self.__refs__[self.__class__].append(weakref.ref(self))

    @classmethod
    def get_instances(cls):
        for inst_ref in cls.__refs__[cls]:
            inst = inst_ref()
            if inst is not None:
                yield inst

class X(KeepRefs):
    def __init__(self, name):
        super(X, self).__init__()
        self.name = name

x = X("x")
y = X("y")
for r in X.get_instances():
    print r.name
del y
for r in X.get_instances():
    print r.name

In this case, all the references get stored as a weak reference in a list. If you create and delete a lot of instances frequently, you should clean up the list of weakrefs after iteration, otherwise there's going to be a lot of cruft.

Another problem in this case is that you have to make sure to call the base class constructor. You could also override __new__, but only the __new__ method of the first base class is used on instantiation. This also works only on types that are under your control.

Edit: The method for printing all instances according to a specific format is left as an exercise, but it's basically just a variation on the for-loops.

Solution 2:

You'll want to create a static list on your class, and add a weakref to each instance so the garbage collector can clean up your instances when they're no longer needed.

import weakref

class A:
    instances = []
    def __init__(self, name=None):
        self.__class__.instances.append(weakref.proxy(self))
        self.name = name

a1 = A('a1')
a2 = A('a2')
a3 = A('a3')
a4 = A('a4')

for instance in A.instances:
    print(instance.name)

Solution 3:

You don't need to import ANYTHING! Just use "self". Here's how you do this

class A:
    instances = []
    def __init__(self):
        self.__class__.instances.append(self)
print('\n'.join(A.instances)) #this line was suggested by @anvelascos

It's this simple. No modules or libraries imported

Solution 4:

Very nice and useful code, but it has a big problem: list is always bigger and it is never cleaned-up, to test it just add print(len(cls.__refs__[cls])) at the end of the get_instances method.

Here a fix for the get_instances method:

__refs__ = defaultdict(list)

@classmethod
def get_instances(cls):
    refs = []
    for ref in cls.__refs__[cls]:
        instance = ref()
        if instance is not None:
            refs.append(ref)
            yield instance
    # print(len(refs))
    cls.__refs__[cls] = refs

or alternatively it could be done using WeakSet:

from weakref import WeakSet

__refs__ = defaultdict(WeakSet)

@classmethod
def get_instances(cls):
    return cls.__refs__[cls]