When You No Longer Need That Object • Dealing With Garbage in Python
Let's explore reference counting and cyclic garbage collection in Python
How often have you heard the phrase "Everything is an object in Python"? We often talk about how to create objects in Python. After all, Python is an object-oriented language (even when you don't define your own classes).
But we don't often talk about how to get rid of objects from memory in Python. And there's a reason for this: Python deals with this task behind the scenes so that you, the programmer, don't need to be concerned about removing objects from memory.
But that's not a good enough reason to ignore what's happening when Python decides to toss an object away! Let's find out how Python deals with its garbage.
Note: the details in this post relate to CPython, the most common implementation of Python.
Before We Take The Rubbish Out
Let's create a class to use throughout this article. We'll work directly in the REPL today:

Nothing too out of the ordinary here (if you're familiar with classes–if not, read A Magical Tour Through Object-Oriented Programming in Python • Hogwarts School of Codecraft and Algorithmancy).
The class Person
has three data attributes:
.name
stores the name of the person (duh!).unique_id
does what it says on the tin. We could use the object's ID that's returned by the built-inid()
, but the.unique_id
provides the possibility of having a more readable unique identifier. In this simple version, we could have just used the.name
attribute, but a unique ID makes it a bit clearer..spouse
will either beNone
or it will store anotherPerson
object.
The .__del__
Special Method
Let's add a special method to this class:
Now, the name of this special method could be slightly misleading. The .__del__()
special method is not directly connected to the del
keyword in the same way that .__len__()
is connected to len()
or .__iter__()
is connected to iter()
, say.
The .__del__()
special method doesn't define what happens when you use del
on the object. Instead, .__del__()
is a finaliser: it's called just before an object is destroyed–just before it's removed from memory.
Therefore, the string within the print()
call in .__del__()
is displayed only when Python is about to remove an object from memory.
Let's try it out:
No surprises here. You create an instance of the Person
class and then delete it right away. The printout shows that Python clears this object from memory.
Reference Counting and Destroying Objects
But there's a bit more happening here than meets the eye. Let's create the object again, but this time you'll also create a new reference to the object:
The second statement does not create a copy of the object. Instead, it creates a second reference to the same object. Behind the scenes, a Python object keeps track of the number of references pointing towards it. In this case, this object has a reference count of two since there are two identifiers (variable names) referring to it: test_person
and another_ref_to_test_person
.
Next, you can delete one of these:
The del
keyword does not delete an object from memory. It removes the identifier (name) that's referring to it. Therefore, the name test_person
is no longer present, and Python decreases the reference count of the Person
instance from two to one.
However, note that there's no printout shown in the code above. The object hasn't been destroyed yet since there's still another reference to it, another_ref_to_test_person
. This name is now the only reference left to the object.
Let's see what happens when you delete this one remaining name:
Now you've removed the last remaining reference for this object. The object's reference count went from one to zero. And since there are no more references left for this object, there's no way for you to access this object. Python removes an object from memory when its reference count goes to zero.
The printout confirms that this object is destroyed when you remove the second reference.
But there's more fun to come on this topic…
The Python Coding Place is offering a 50% discount on a 12-course bundle of Python courses.
Get all 12 video courses for $200 instead of $400, and you also get a free e-copy of The Python Coding Book bundled in and access to the exclusive forum.
You can see the list of courses included on the Python Bundle page.
Cyclic References
Let's look at another example using the same class. I'm showing the class definition again below, but there are no changes to the class:
You create a new instance of the Person
class. You may be familiar with this person! Let's use a diagram to represent this object:
The orange/yellow box represents the object. The three data attributes are shown within the box. Also shown is the reference count. At the moment, there's only one reference to this object: the identifier homer
. You can see an arrow linking the identifier homer
to the object.
Next, create a second instance:
Note that the third argument is not None
in this case. You pass homer
as the third argument. This is the argument that's assigned to the .spouse
data attribute. Homer is Marge's husband!
Let's update the diagram:
There are a few updates to this diagram:
There's the new object shown in purple with the identifier
marge
pointing towards it.This new object has a reference count of 1 since there's only one reference pointing to it.
The data attributes are also shown in the purple box.
But note the
.spouse
data attribute is a reference to the yellow box, the Homer Simpson object. There's an arrow pointing from the purple box to the yellow box to represent this.The yellow box now shows a reference count of 2 since there are two references to the Homer Simpson object: the original
homer
identifier and themarge.spouse
data attribute. There are two arrows pointing towards the yellow box.
But let's also add Marge Simpson as Homer Simpson's wife:
The data attribute homer.spouse
is no longer None
. It now refers to the same object that the identifier marge
refers to. Let's update the diagram:
Now, there's an arrow pointing from the Homer Simpson yellow box to the Marge Simpson purple box. Therefore, there's a second reference pointing to the marge
object. Its reference count is now 2.
Both objects now have two references. The identifiers homer
and marge
point to their respective objects. But homer.spouse
and marge.spouse
also each point to the other object.
You can confirm that each object points to the other through their .spouse
data attributes:
Since the Person
class doesn't have a .__str__()
or a .__repr__()
special method, you can't just display homer.spouse
or marge.spouse
. Well, you can, but it won't give you any helpful information in this case. Instead, you can display the .name
attribute directly.
The Cyclic Garbage Collector
Time to start deleting identifiers (variable names) and see what happens to the objects. Let's start by deleting homer
:
Note how there's no printout saying that this object is about to be deleted from memory. You delete the identifier homer
, which removes one of the two references to the Homer Simpson object:
Although you've removed one of the arrows pointing towards the yellow box, there's still another arrow pointing to this object–the one from the Marge Simpson object! The reference count for the Homer Simpson object is now one. Therefore, Python doesn't remove this object since the reference count is not zero.
Next, it's time to delete marge
:
Still no printout. No object is being destroyed yet. Let's see why:
You delete the marge
identifier, and therefore, you remove one of the references to the Marge Simpson object. But there's still a reference to this object, the one coming from the Homer Simpson object. Therefore, the object is not deleted since the reference count is not zero.
But…
…there's a problem. Let's look at the status of our objects as they stand now:
Both objects have a reference count of one, which is why they haven't been deleted yet. But neither object has an identifier you can use to access it. The only way to access the Marge Simpson object is through the Homer Simpson object. But–you see where this is going?–the only way to access the Homer Simpson object is through the Marge Simpson object.
You can't get to either object. They're unreachable. But they're still there in memory, keeping each other alive.
Python's mechanism of destroying objects when their reference count reaches zero doesn't kick in here. That's why you didn't get any printouts showing that any of the objects were about to be deleted.
But Python has another mechanism that looks out for this cyclic reference situation. The cyclic garbage collector periodically searches for these types of objects.
However, it won't run instantly, and you have no guarantees on when this garbage collection occurs. But it will happen at some point!
You can force the cyclic garbage collection to run using the gc
module:
The cyclic garbage collector found the two unreachable objects–the 2
on the final line shows how many unreachable objects it found. And you also see the two printouts showing that both the Homer Simpson and Marge Simpson objects are about to be destroyed.
Final Words
So, Python will remove an object from memory when its reference count reaches zero. If the reference count is zero, there's no way to access the object. Therefore, there's no point in keeping it alive, as it would be using up precious resources.
But you can have cyclic references for objects whose reference counts never reach zero. However, the objects are still unreachable, as was the case with the Homer and Marge objects above. Python's cyclic garbage collection keeps an eye out for these unreachable objects and eventually destroys them.
Do you need to know all this to write good Python programs? No. But you may encounter situations where this knowledge can help you understand what's happening behind the scenes in your program.
Do you want to join a forum to discuss Python further with other Pythonistas? Upgrade to a paid subscription here on The Python Coding Stack to get exclusive access to The Python Coding Place's members' forum. More Python. More discussions. More fun.
And you'll also be supporting this publication. I put plenty of time and effort into crafting each article. Your support will help me keep this content coming regularly and, importantly, will help keep it free for everyone.
Photo by Thirdman : https://www.pexels.com/photo/a-man-holding-a-sack-of-garbage-7656998/
Code in this article uses Python 3.13
The code images used in this article are created using Snappify. [Affiliate link]
You can also support this publication by making a one-off contribution of any amount you wish.
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com
Further reading related to this article’s topic:
When Should You Use
.__repr__()
vs.__str__()
in Python? – Real PythonIterable: Python's Stepping Stones (Data Structure Categories #1)
Appendix: Code Blocks
Code Block #1
class Person:
def __init__(self, name, unique_id, spouse):
self.name = name
self.unique_id = unique_id
self.spouse = spouse
Code Block #2
class Person:
def __init__(self, name, unique_id, spouse):
self.name = name
self.unique_id = unique_id
self.spouse = spouse
def __del__(self):
print(
f"Object {self.unique_id!r} is about to be "
f"removed from memory. Goodbye!"
)
Code Block #3
# ...
test_person = Person("Test Person", "test001", None)
del test_person
# Object 'test001' is about to be removed from memory. Goodbye!
Code Block #4
# ...
test_person = Person("Test Person", "test001", None)
another_ref_to_test_person = test_person
Code Block #5
# ...
del test_person
Code Block #6
# ...
del another_ref_to_test_person
# Object 'test001' is about to be removed from memory. Goodbye!
Code Block #7
class Person:
def __init__(self, name, unique_id, spouse):
self.name = name
self.unique_id = unique_id
self.spouse = spouse
def __del__(self):
print(
f"Object {self.unique_id!r} is about to be "
f"removed from memory. Goodbye!"
)
homer = Person("Homer Simpson", "homer001", None)
Code Block #8
# ...
marge = Person("Marge Simpson", "marge002", homer)
Code Block #9
# ...
homer.spouse = marge
Code Block #10
# ...
homer.spouse.name
# 'Marge Simpson'
marge.spouse.name
# 'Homer Simpson'
Code Block #11
# ...
del homer
Code Block #12
# ...
del marge
Code Block #13
# ...
import gc
gc.collect()
# Object 'homer001' is about to be removed from memory. Goodbye!
# Object 'marge002' is about to be removed from memory. Goodbye!
# 2
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com