Skip to main content

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:

  1. instance_count is a class variable shared by all instances. It belongs to the class itself, not to any individual object.
  2. Each time __init__ is called (i.e., a new object is created), the counter increments.
  3. Access it via User.instance_count (on the class) or user1.instance_count (on any instance).
Important: Use ClassName.counter, Not self.counter

Always 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)
note

__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
Why Use 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
note

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

MethodTracks Live InstancesHandles DeletionAccess to InstancesComplexity
Class variable counter❌ (total only)Simple
Counter + __del__Moderate
weakref set✅ (automatic)Advanced
Separate counters per subclassOptionalModerate

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 weakref when 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.