Of course! The term "subscriptable" in Python refers to an object's ability to have its elements accessed using square brackets []. This is a fundamental concept in Python.

Let's break it down in detail.
The Core Idea: What Does "Subscriptable" Mean?
When you see the error TypeError: 'X' object is not subscriptable, it means you are trying to use the square bracket notation [] on an object X that doesn't support it.
Think of it like a book:
- A list, tuple, or dictionary is like a book with an index or a table of contents. You can look up a specific page or entry using its number or key (e.g.,
my_list[5],my_dict['name']). These are subscriptable. - An integer or a string (in some contexts) is like a single, standalone chapter. You can't look up a "page 5" within that single chapter because it doesn't have an internal index of its own parts. These are generally not subscriptable.
How to Check if an Object is Subscriptable
The most reliable way to check is to use the built-in isinstance() function with the collections.abc.Sequence or collections.abc.Mapping abstract base classes. These classes define the standard interface for sequences (like lists, tuples) and mappings (like dictionaries), respectively.

import collections.abc
# Subscriptable objects
my_list = [1, 2, 3]
my_tuple = (1, 2, 3)
my_dict = {'a': 1, 'b': 2}
my_string = "hello"
my_set = {1, 2, 3}
# Non-subscriptable objects
my_integer = 123
my_float = 45.67
my_boolean = True
print(f"Is a list subscriptable? {isinstance(my_list, collections.abc.Sequence)}") # True
print(f"Is a tuple subscriptable? {isinstance(my_tuple, collections.abc.Sequence)}") # True
print(f"Is a dict subscriptable? {isinstance(my_dict, collections.abc.Mapping)}") # True
print(f"Is a string subscriptable? {isinstance(my_string, collections.abc.Sequence)}") # True
print(f"Is a set subscriptable? {isinstance(my_set, collections.abc.Sequence)}") # True
print("-" * 20)
print(f"Is an int subscriptable? {isinstance(my_integer, collections.abc.Sequence)}") # False
print(f"Is a float subscriptable? {isinstance(my_float, collections.abc.Sequence)}") # False
print(f"Is a bool subscriptable? {isinstance(my_boolean, collections.abc.Sequence)}") # False
Common Subscriptable Data Types
Here are the most common subscriptable objects you'll use, along with how you subscript them.
| Data Type | Example | How to Subscript | Description |
|---|---|---|---|
list |
my_list = [10, 20, 30] |
my_list[0] |
Accesses an element by its integer index (starts at 0). |
tuple |
my_tuple = ('a', 'b', 'c') |
my_tuple[1] |
Accesses an element by its integer index (immutable). |
str |
my_string = "python" |
my_string[3] |
Accesses a character by its integer index. |
dict |
my_dict = {'key1': 'val1'} |
my_dict['key1'] |
Accesses a value by its key. |
set |
my_set = {1, 2, 3} |
my_set[0] |
ERROR! Sets are unordered and not subscriptable by index. |
The TypeError: 'X' object is not subscriptable
This is the error you'll encounter most often. Let's look at common scenarios that cause it.
Scenario 1: Trying to subscript an integer or float
This is a very common mistake, especially when a function is expected to return a number but instead returns a list or tuple.
def get_first_item(items):
# This function is supposed to return the first item,
# but it incorrectly returns a list containing the first item.
return [items[0]]
my_data = [10, 20, 30]
first_element = get_first_item(my_data)
print(f"The function returned: {first_element}")
print(f"The type of the returned value is: {type(first_element)}")
# Now, let's try to subscript it again, thinking it's the number 10.
# This will fail because first_element is a list, not an integer.
try:
# This is the line that causes the error
value = first_element[0] + 5
except TypeError as e:
print(f"\nError caught: {e}")
# The correct way would be to access the element inside the list first
correct_value = first_element[0] + 5
print(f"The correct value is: {correct_value}")
Output:

The function returned: [10]
The type of the returned value is: <class 'list'>
Error caught: 'int' object is not subscriptable
The correct value is: 15
Wait, the error message says 'int' object is not subscriptable, but we were trying to subscript a list!
This highlights a subtle but important point: The error message refers to the type of the object you are trying to subscript. In this case, the error occurred inside the function get_first_item when it tried to do items[0]. If items was, for example, the integer 123, the error would have been raised there. The key takeaway is to always check the type of your variable before trying to subscript it.
Scenario 2: Confusing a variable name with a function call
Sometimes you might have a variable and a function with the same name, leading to confusion.
# Let's say we have a list named 'data'
data = [1, 2, 3]
# And a function that also happens to be named 'data'
def data():
return "I am a function, not a list!"
# If you call the function without parentheses, you get the function object itself
# This function object is not subscriptable.
try:
print(data[0])
except TypeError as e:
print(f"Error: {e}")
print("You likely meant to call the function with parentheses: data()")
Output:
Error: 'function' object is not subscriptable
You likely meant to call the function with parentheses: data()
How to Make Your Own Objects Subscriptable
You can create your own custom classes that are subscriptable by implementing special "magic" methods in your class.
- For integer indexing (like a list or tuple), implement
__getitem__(self, key). - For assignment (e.g.,
my_obj[0] = 'new_value'), implement__setitem__(self, key, value). - For deletion (e.g.,
del my_obj[0]), implement__delitem__(self, key).
Example: A Custom Countdown Class
Let's create a class that behaves like a list, counting down from a given number.
class Countdown:
def __init__(self, start):
if not isinstance(start, int):
raise TypeError("Start value must be an integer.")
self.current = start
def __iter__(self):
# This makes the object iterable, so you can use it in a for loop
return self
def __next__(self):
# This is part of making it iterable
if self.current < 0:
raise StopIteration
else:
value = self.current
self.current -= 1
return value
def __getitem__(self, index):
# This makes the object subscriptable!
if not isinstance(index, int):
raise TypeError("Index must be an integer.")
if index < 0 or index >= self.current + 1:
raise IndexError("Index out of range for countdown.")
# The value at a given index is start - index
return self.current + 1 - (index + 1) # Simplifies to self.current - index
# --- Usage ---
try:
# Create a countdown from 5
countdown = Countdown(5)
# Now it's subscriptable!
print(f"The first item is: {countdown[0]}") # Output: 5
print(f"The third item is: {countdown[2]}") # Output: 3
print(f"The last item is: {countdown[5]}") # Output: 0
# You can also iterate over it
print("\nIterating over the countdown:")
for number in countdown:
print(number)
except (TypeError, IndexError) 