How to Count the Number of Instances of a Class in Python
Tracking how many objects have been created from a class is a common requirement in Python, whether you're managing resource pools, implementing the observer pattern, debugging memory usage, monitoring application state, or enforcing instance limits.
In this guide, you'll learn multiple methods to count class instances in Python, from simple class variables to more robust approaches that handle instance deletion.
Using a Class Variable as a Counter
The most straightforward approach is to use a class variable that increments every time the constructor (__init__) is called:
class User:
instance_count = 0
def __init__(self, name):
self.name = name
User.instance_count += 1
# Create instances
user1 = User("Alice")
user2 = User("Bob")
user3 = User("Charlie")
print(f"Total User instances: {User.instance_count}")
Output:
Total User instances: 3
How it works:
instance_countis a class variable shared by all instances. It belongs to the class itself, not to any individual object.- Each time
__init__is called (i.e., a new object is created), the counter increments. - Access it via
User.instance_count(on the class) oruser1.instance_count(on any instance).
ClassName.counter, Not self.counterAlways increment using the class name (User.instance_count += 1), not self:
class Example:
count = 0
def __init__(self):
# ❌ Wrong: creates a new instance variable, doesn't update class variable
self.count += 1
e1 = Example()
e2 = Example()
print(Example.count) # Still 0!
Output:
0
self.count += 1 creates a new instance attribute on each object instead of modifying the shared class variable. Always use Example.count += 1.
Tracking Instance Deletion with __del__
The basic counter only increments. It doesn't decrease when objects are deleted. To accurately track live instances, decrement the counter in the destructor:
class Connection:
active_count = 0
def __init__(self, host):
self.host = host
Connection.active_count += 1
print(f"Connected to {host} (active: {Connection.active_count})")
def __del__(self):
Connection.active_count -= 1
print(f"Disconnected from {self.host} (active: {Connection.active_count})")
conn1 = Connection("server-1")
conn2 = Connection("server-2")
conn3 = Connection("server-3")
print(f"\nActive connections: {Connection.active_count}")
# Delete one connection
del conn2
print(f"Active connections: {Connection.active_count}")
Output:
Connected to server-1 (active: 1)
Connected to server-2 (active: 2)
Connected to server-3 (active: 3)
Active connections: 3
Disconnected from server-2 (active: 2)
Active connections: 2
Disconnected from server-1 (active: 1)
Disconnected from server-3 (active: 0)
__del__ is called when an object is garbage collected, not necessarily immediately when del is used. In CPython (the standard implementation), del typically triggers immediate cleanup, but this behavior isn't guaranteed in other Python implementations.
Tracking Both Total and Active Instances
For comprehensive monitoring, track both the total number of instances ever created and the current number of live instances:
class Task:
total_created = 0
active_count = 0
def __init__(self, name):
self.name = name
Task.total_created += 1
Task.active_count += 1
def __del__(self):
Task.active_count -= 1
@classmethod
def stats(cls):
print(f"Total created: {cls.total_created}")
print(f"Currently active: {cls.active_count}")
print(f"Deleted: {cls.total_created - cls.active_count}")
t1 = Task("Build")
t2 = Task("Test")
t3 = Task("Deploy")
del t2
Task.stats()
Output:
Total created: 3
Currently active: 2
Deleted: 1
Using a Set to Track All Live Instances
If you need access to the actual instances (not just the count), store weak references to avoid preventing garbage collection:
import weakref
class Sensor:
_instances = set()
def __init__(self, sensor_id):
self.sensor_id = sensor_id
Sensor._instances.add(weakref.ref(self, Sensor._remove_ref))
@classmethod
def _remove_ref(cls, ref):
cls._instances.discard(ref)
@classmethod
def count(cls):
return len(cls._instances)
@classmethod
def all_instances(cls):
return [ref() for ref in cls._instances if ref() is not None]
s1 = Sensor("temp-01")
s2 = Sensor("humidity-02")
s3 = Sensor("pressure-03")
print(f"Active sensors: {Sensor.count()}")
print(f"Sensor IDs: {[s.sensor_id for s in Sensor.all_instances()]}")
del s2
print(f"After deletion: {Sensor.count()}")
Output:
Active sensors: 3
Sensor IDs: ['pressure-03', 'temp-01', 'humidity-02']
After deletion: 2
weakref?Regular references in a class-level set would prevent objects from being garbage collected even after all other references are removed. weakref allows the garbage collector to clean up objects normally while still letting you track them.
Counting Instances in Subclasses
When using inheritance, decide whether subclass instances should count toward the parent's total:
class Animal:
count = 0
def __init__(self, name):
self.name = name
Animal.count += 1
class Dog(Animal):
dog_count = 0
def __init__(self, name, breed):
super().__init__(name)
Dog.dog_count += 1
self.breed = breed
class Cat(Animal):
cat_count = 0
def __init__(self, name, color):
super().__init__(name)
Cat.cat_count += 1
self.color = color
d1 = Dog("Rex", "Labrador")
d2 = Dog("Max", "Poodle")
c1 = Cat("Whiskers", "Orange")
print(f"Total animals: {Animal.count}")
print(f"Dogs: {Dog.dog_count}")
print(f"Cats: {Cat.cat_count}")
Output:
Total animals: 3
Dogs: 2
Cats: 1
Each class tracks its own count, while Animal.count tracks the total across all animal types.
Practical Example: Connection Pool
A real-world use case is limiting the number of active connections:
class DatabaseConnection:
MAX_CONNECTIONS = 3
active_count = 0
def __init__(self, db_name):
if DatabaseConnection.active_count >= DatabaseConnection.MAX_CONNECTIONS:
raise RuntimeError(
f"Connection limit reached ({DatabaseConnection.MAX_CONNECTIONS}). "
"Close an existing connection first."
)
self.db_name = db_name
DatabaseConnection.active_count += 1
print(f"Connected to '{db_name}' ({DatabaseConnection.active_count}/{DatabaseConnection.MAX_CONNECTIONS})")
def close(self):
DatabaseConnection.active_count -= 1
print(f"Closed '{self.db_name}' ({DatabaseConnection.active_count}/{DatabaseConnection.MAX_CONNECTIONS})")
def __del__(self):
# Safety net in case close() wasn't called
if DatabaseConnection.active_count > 0:
DatabaseConnection.active_count -= 1
conn1 = DatabaseConnection("users_db")
conn2 = DatabaseConnection("orders_db")
conn3 = DatabaseConnection("analytics_db")
try:
conn4 = DatabaseConnection("logs_db")
except RuntimeError as e:
print(f"\nError: {e}")
conn2.close()
conn4 = DatabaseConnection("logs_db")
Output:
Connected to 'users_db' (1/3)
Connected to 'orders_db' (2/3)
Connected to 'analytics_db' (3/3)
Error: Connection limit reached (3). Close an existing connection first.
Closed 'orders_db' (2/3)
Connected to 'logs_db' (3/3)
Quick Comparison of Methods
| Method | Tracks Live Instances | Handles Deletion | Access to Instances | Complexity |
|---|---|---|---|---|
| Class variable counter | ❌ (total only) | ❌ | ❌ | Simple |
Counter + __del__ | ✅ | ✅ | ❌ | Moderate |
weakref set | ✅ | ✅ (automatic) | ✅ | Advanced |
| Separate counters per subclass | ✅ | Optional | ❌ | Moderate |
Conclusion
Counting class instances in Python is straightforward using class variables that increment in __init__:
- Use a simple class counter when you only need the total number of objects ever created.
- Add a
__del__method to decrement the counter when you need to track currently active instances. - Use
weakrefwhen you need access to the actual live instances without preventing garbage collection. - Use separate counters in parent and child classes when you need to track instances per type in an inheritance hierarchy.
For most applications, a class variable with increment in __init__ and decrement in __del__ provides the right balance of simplicity and accuracy.