Hermione's Undetectable Extension Charm: Revealed (Using Python)
How could Hermione possibly fit all those things in her small handbag in HP:DH? It's linked to objects and their references in Python, of course
Raise your hand if you're a Harry Potter fan. Those of you with your hands still down–what are we going to do with you?! I'll let you stay and read on, but seriously…
There are several uses of this charm in the Harry Potter books. Perhaps the most memorable is Hermione's small bag in the final book, Harry Potter and the Deathly Hallows. The handbag could hold numerous large objects but still appeared as a small, light handbag to the outside world.
Magic? Not quite. So, after all these years, I can finally reveal how this charm really works.
The Handbag
Let's create the handbag. A Python list will do for this, but you can use other data structures if you prefer:

This is an empty handbag. It's small and lightweight. Here's proof:
The getsizeof()
function in the sys
module does what it says on the tin. The handbag
is 56 bytes large–or 56 bytes small, I should say. Fifty-six is small, tiny, minuscule.
The Large Object
Hermione put books–loads of them–potions, tents, and other stuff in her bag. Let's put a massive list of numbers in ours.
Let's create this object first:
This object is large. Very large. Here's proof:
That's almost 90 million bytes. Ninety million! That's a lot, right?!
Placing the Large Object in the Small Handbag
Very small bag: check
Very large object: check
Let's put the large object inside the small bag:
As you can see, even in this truncated output, the large object is in the bag.
So, has the bag expanded in order to hold the object? That would hardly be useful for Hermione and her Undetectable Extension Charm.
Here's the size of the bag now:
The handbag is still small and lightweight. Eighty-eight bytes. Sure, it's a tiny bit bigger than it was before, but nowhere close to the nearly 90,000,000 bytes of the object it contains.
Magic, as I said!
The Trick Behind the Magic
There's nothing like revealing the trick to spoil the magic. So, don't read on if you believe Father Christmas comes down your chimney on Christmas Day or that the tooth fairy takes your tooth from under your pillow and leaves a coin–or indeed if you believe in Undetectable Extension Charms.
When you create an object in Python, it lives in a chunk of memory somewhere on your computer. You then create references to this object, such as variable names (or identifiers, as Python calls them). Passing the object to a function also creates a new reference, as does placing the object in another data structure.
So, when you created the large list of numbers above, Python created an object of type list containing 10 million items and created the identifier large_object
, which refers to this list object.
And when you append this large list to the handbag
list…
Actually, let's pause here. We'll finish that thought later. Let's look at the line that appends large_list
to handbag
:
Let me give you a dramatised version of what Python does as it reads and executes this line. I'm choosing to personify Python in this narrative. But those of you familiar with my Monty and the White Room analogy, then you can think of Monty performing these tasks:
Python reads the name
handbag
. This is a reference to an object in memory–an empty list–that was created moments earlier when you executedhandbag = []
.The program then moves on to
.append
and looks for this attribute in the object of type list that has the referencehandbag
. I'm using language that's excessively verbose here to stress the links between references and objects.Python notes that
.append
is a method. The parentheses show that the program ought to call the method.It goes on to read the identifier
large_object
. It looks for the object referenced by this name and finds the object of type list that contains 10 million items.Finally, Python adds a new reference to this object–the list with 10 million items. This new reference is the first item in the list
handbag
(the list that's referenced by the identifierhandbag
, if you prefer). So, the large list now has two references:large_object
andhandbag[0]
.
So, the handbag
doesn't contain the large_object
. It contains a reference to it. That's why handbag
is still small. It's larger than an empty list since it now has something within it–the reference to the large list–but it's only a reference and not the entire list.
Incidentally, when you add a reference–the one pointing to the large list–to the empty list, handbag
, CPython creates space for four items rather than one. But that's another story for another day, perhaps.
Also, incidentally, when I wrote "the object of type list that contains 10 million items" when referring to large_object
, those items are themselves references to the integer objects they represent.
And a final 'incidentally' if you allow me. You may have noticed I've italicised contains each time I used it to refer to a data structure containing another object. The data structure technically contains the reference to the object and not the object itself, as we discussed above.
But in everyday parlance in Python, you can safely say that "handbag
contains large_object
" rather than–wait for this–"The object referenced by the identifier handbag
contains a reference to the object that's referenced by the identifier large_object
". Aren't you glad the shorter, less precise version is acceptable!
In fact, Python even returns True
if you ask whether large_object
is contained in handbag
:
This is similar to the following call:
As you can see, even Python is happy with the fiction that the handbag
contains the large_object
. What would life be without these little fictions we convince ourselves to believe!
Before you get to my Final Words in this article, allow me a minute of your time. If you enjoyed this article, if you feel you learned more about how Python works, if you had a Wow! moment, would you take a moment to consider upgrading to a paid subscription? Just consider it. It's fine if you decide you don't want to.
Or you can make a one-off contribution of any amount you wish, whether it's $1 or $1,000–right, I'm only joking with the top end of that scale, of course–but if you want to, I won't stop you. Seriously, any amount you wish, or nothing at all–that's also fine. I'll still keep writing!
Final Words
I wrote about this topic on other occasions on The Python Coding Stack. If you're an avid reader of The Stack, then:
You shouldn't have been surprised by this magic trick, and…
…apologies for writing about this topic again. However, I feel that this new narrative gives a different perspective that can help strengthen the reader's understanding of what's going on behind the scenes in Python.
Here are two previous articles that cover this idea: What's In A Name? and What's In A List—Yes, But What's Really In A Python List. Indeed, even last week's article is related: The One About the £5 Note and the Trip to the Coffee Shop • The Difference Between `is` and `==` in Python.
Data structures contain references to the objects they contain. That's it. That's your summary to conclude this short post.
Code in this article uses Python 3.13
The code images used in this article are created using Snappify. [Affiliate link]
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:
The One About the £5 Note and the Trip to the Coffee Shop • The Difference Between
is
and==
in PythonUnrelated to this week's Python topic, but also Harry Potter-themed–a seven-part series: A Magical Tour Through Object-Oriented Programming in Python • Hogwarts School of Codecraft and Algorithmancy
Appendix: Code Blocks
Code Block #1
handbag = []
Code Block #2
import sys
sys.getsizeof(handbag)
# 56
Code Block #3
import random
large_object = [
random.randint(-100, 100) for _ in range(10_000_000)
]
Code Block #4
sys.getsizeof(large_object)
# 89095160
Code Block #5
handbag.append(large_object)
handbag
# [[-57, 60, 89, 35, -58, -76, -71, 0, -13,
# -19, 5, 3, 35, -97, 81, -65, -96, -20,
# -42, -3, 11, -40, -82, -30, -97, -89,
# ...
# -58, -74, 36, -77, -10, -11, 61, -39,
# 57, -12, 33, -99, 46, 2, -84, -51, 42]]
Code Block #6
sys.getsizeof(handbag)
# 88
Code Block #7
handbag.append(large_object)
Code Block #8
large_object in handbag
# True
Code Block #9
handbag.__contains__(large_object)
# True
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