Reputation: 279
I want to keep track of all of the class instances that derive from a base class, and I'm curious if the following way is common, or if it presents serious pitfalls. That is, pattern or antipattern?
class Animal(object):
all_animals = []
def __init__(self):
self.all_animals.append(self)
class Dog(Animal):
def __init__(self):
# Do stuff here...
super(Dog, self).__init__()
class Person(Animal):
def __init__(self):
# Do other stuff here...
super(Person, self).__init__()
Is this a recommended way to do things? Does this method have any trouble when the inheritance happens over a large project or a number of files? For example, can I rely on a consistent Animal.all_animals
cache?
Upvotes: 1
Views: 241
Reputation: 250931
One issue in your approach is that the list will prevent the instances from being garbage collected once there are no more references left to them. So, if the instances are hashable and order of instances doesn't matter then we can use weakref.WeakSet
to store the instances and instead of storing the instances in __init__
method we can use a Metaclass to do this whenever an instance is created.(You can use the weakreflist package from PyPI if you want a list)
When we instantiate a class its Metaclass's __call__
method is called first, there we can store the instance in the WeakSet
. Apart from this it would make more sense if we allow all_animals
to be accessed only from Animal
class, so I've used a descriptor whose __get__
method will only allow us to access it from Animal
class.
from weakref import WeakSet
class Meta(type):
def __call__(self):
instance = super(Meta, self).__call__()
Animal.all_animals.add(instance)
return instance
class Animals_prop(object):
def __init__(self, cls=WeakSet):
self.all_animals = cls()
def __get__(self, ins, cls):
if ins is None and cls is Animal:
return self.all_animals
else:
raise AttributeError
class Animal(object):
__metaclass__ = Meta
all_animals = Animals_prop()
def __del__(self):
print "{}'s instance is dead.".format(type(self).__name__)
class Dog(Animal):
pass
class Person(Animal):
pass
if __name__ == '__main__':
a = Animal()
b = Dog()
c = Person()
print set(Animal.all_animals)
del c
print set(Animal.all_animals)
print '-'*10
Output:
set([<__main__.Animal object at 0x1012f5590>, <__main__.Dog object at 0x1012f55d0>, <__main__.Person object at 0x1012f5610>])
Person's instance is dead.
set([<__main__.Animal object at 0x1012f5590>, <__main__.Dog object at 0x1012f55d0>])
----------
Animal's instance is dead.
Dog's instance is dead.
Upvotes: 1