How to Resolve Python "TypeError: 'X' object is not subscriptable"
In Python, the TypeError: 'X' object is not subscriptable
(where 'X' might be dict_keys
, dict_values
, dict_items
, generator
, map
, filter
, zip
, etc.) indicates that you are trying to access an element using square bracket indexing (object[index]
) on an object that doesn't support this operation directly. These types of objects are often iterators or views, not sequences like lists or tuples that store all elements with accessible indices.
This guide explains why this error occurs for common non-subscriptable iterables like dict_keys
, generators, and map objects, and provides the standard solution: converting them to lists first.
Understanding the Error: Subscriptable vs. Non-Subscriptable
- Subscriptable objects are containers whose items can be accessed using square brackets (
[]
) with an index (for sequences like lists, tuples, strings) or a key (for mappings like dictionaries). They support the__getitem__
special method. - Non-subscriptable objects include many iterators and view objects. They allow you to loop over their items (
for item in object:
), but they don't necessarily store all items in memory at once or provide direct access by index. Attemptingobject[index]
on them raises theTypeError
.
Error with Dictionary Views (dict_keys
, dict_values
, dict_items
)
Cause: View Objects are Not Lists
In modern Python, methods like my_dict.keys()
, my_dict.values()
, and my_dict.items()
return dynamic view objects, not lists. These views reflect the current state of the dictionary but don't support direct indexing.
my_dictionary = {'fruit': 'apple', 'color': 'red', 'count': 5}
# Get the view objects
keys_view = my_dictionary.keys()
values_view = my_dictionary.values()
items_view = my_dictionary.items()
print(f"Keys view: {keys_view} (Type: {type(keys_view)})")
# Output: Keys view: dict_keys(['fruit', 'color', 'count']) (Type: <class 'dict_keys'>)
print(f"Values view: {values_view} (Type: {type(values_view)})")
# Output: Values view: dict_values(['apple', 'red', 5]) (Type: <class 'dict_values'>)
print(f"Items view: {items_view} (Type: {type(items_view)})")
# Output: Items view: dict_items([('fruit', 'apple'), ('color', 'red'), ('count', 5)]) (Type: <class 'dict_items'>)
# Error Scenario: Trying to index a view object
try:
# ⛔️ TypeError: 'dict_keys' object is not subscriptable
first_key = keys_view[0]
print(first_key)
except TypeError as e:
print(f"\nError accessing keys_view[0]: {e}")
try:
# ⛔️ TypeError: 'dict_values' object is not subscriptable
first_value = values_view[0]
print(first_value)
except TypeError as e:
print(f"Error accessing values_view[0]: {e}")
Output:
Keys view: dict_keys(['fruit', 'color', 'count']) (Type: <class 'dict_keys'>)
Values view: dict_values(['apple', 'red', 5]) (Type: <class 'dict_values'>)
Items view: dict_items([('fruit', 'apple'), ('color', 'red'), ('count', 5)]) (Type: <class 'dict_items'>)
Error accessing keys_view[0]: 'dict_keys' object is not subscriptable
Error accessing values_view[0]: 'dict_values' object is not subscriptable
Solution: Convert to list()
Before Indexing
To access elements by index, explicitly convert the view object into a list using the list()
constructor.
my_dictionary = {'fruit': 'apple', 'color': 'red', 'count': 5}
# Convert keys view to a list
keys_list = list(my_dictionary.keys())
print(f"\nKeys as list: {keys_list}")
# Access by index works on the list
first_key = keys_list[0]
second_key = keys_list[1]
print(f"First key: '{first_key}'")
print(f"Second key: '{second_key}'")
# Convert values view to a list
values_list = list(my_dictionary.values())
print(f"\nValues as list: {values_list}")
first_value = values_list[0]
print(f"First value: '{first_value}'")
# Convert items view to a list
items_list = list(my_dictionary.items())
print(f"\nItems as list: {items_list}")
first_item_tuple = items_list[0]
print(f"First item tuple: {first_item_tuple}")
print(f"Key of first item: {first_item_tuple[0]}") # Access element within the tuple
Output:
Keys as list: ['fruit', 'color', 'count']
First key: 'fruit'
Second key: 'color'
Values as list: ['apple', 'red', 5]
First value: 'apple'
Items as list: [('fruit', 'apple'), ('color', 'red'), ('count', 5)]
First item tuple: ('fruit', 'apple')
Key of first item: fruit
Once converted to a list, standard indexing and slicing work as expected. Remember that dictionary order is guaranteed only in Python 3.7+.
Iterating Over Views
You can iterate directly over view objects without converting them to lists, which is often more efficient if you just need to loop through them.
my_dictionary = {'fruit': 'apple', 'color': 'red', 'count': 5}
print("Iterating over keys view:")
for key in my_dictionary.keys():
print(key)
print()
print("Iterating over items view:")
for key, value in my_dictionary.items():
print(f" {key}: {value}")
Output:
Iterating over keys view:
fruit
color
count
Iterating over items view:
fruit: apple
color: red
count: 5
Error with Generators (generator
object)
Cause: Generators are Lazy Iterators
Generator functions (using yield
) and generator expressions ((x for x in...)
) create generator objects. These are iterators that produce values one at a time ("lazily") when requested (e.g., in a for
loop or via next()
). They don't store all values in memory at once and don't support indexing.
# Example generator function
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
# Create a generator object
my_generator = count_up_to(3)
print(f"Generator object: {my_generator}")
print(f"Type: {type(my_generator)}") # Output: <class 'generator'>
# Error Scenario: Trying to index the generator
try:
# ⛔️ TypeError: 'generator' object is not subscriptable
first_val = my_generator[0]
print(first_val)
except TypeError as e:
print(f"Error accessing generator[0]: {e}")
Output:
Generator object: <generator object count_up_to at 0x7d37a613a200>
Type: <class 'generator'>
Error accessing generator[0]: 'generator' object is not subscriptable
Solution: Convert to list()
Before Indexing (Consumes Generator)
Convert the generator to a list to access elements by index.
This exhausts the generator; all its values are computed and stored in the list. You cannot iterate over the original generator object again after converting it to a list.
def count_up_to(n):
i = 1; yield i; i += 1; yield i; i += 1; yield i # Condensed for example
my_generator = count_up_to(3)
# Convert generator to list
generator_as_list = list(my_generator)
print(f"Generator as list: {generator_as_list}") # Output: [1, 2, 3]
# Access by index works on the list
first_val = generator_as_list[0]
second_val = generator_as_list[1]
print(f"First value from list: {first_val}") # Output: 1
print(f"Second value from list: {second_val}") # Output: 2
# The original generator is now exhausted
try:
print(f"Trying next() on original generator: {next(my_generator)}")
except StopIteration:
print("Original generator is exhausted after list conversion.")
Output:
Generator as list: [1, 2, 3]
First value from list: 1
Second value from list: 2
Original generator is exhausted after list conversion.
Alternatives: Iteration with next()
or for
loop
If you only need values sequentially, iterate directly:
def count_up_to(n):
i = 1; yield i; i += 1; yield i; i += 1; yield i
my_generator = count_up_to(3)
print("Iterating with next():")
print(next(my_generator)) # Output: 1
print(next(my_generator)) # Output: 2
print("\nIterating with for loop:")
my_generator2 = count_up_to(3)
for value in my_generator2:
print(value) # Output: 1, 2, 3
Output:
Iterating with next():
1
2
Iterating with for loop:
1
2
3
Error with map
Objects
Cause: map()
Returns an Iterator
In Python 3, the built-in map(function, iterable)
applies the function to each item but returns a map
object, which is an iterator, not a list. Like generators, map objects produce values lazily and are not subscriptable.
numbers_str = ['1', '2', '3', '4']
# Create a map object to convert strings to integers
map_obj = map(int, numbers_str)
print(f"Map object: {map_obj}")
print(f"Type: {type(map_obj)}") # Output: <class 'map'>
# Error Scenario: Trying to index the map object
try:
# ⛔️ TypeError: 'map' object is not subscriptable
first_num = map_obj[0]
print(first_num)
except TypeError as e:
print(f"Error accessing map_obj[0]: {e}")
Output:
Map object: <map object at 0x79833acd56c0>
Type: <class 'map'>
Error accessing map_obj[0]: 'map' object is not subscriptable
Solution: Convert to list()
Before Indexing
Convert the map
object to a list to allow indexing. Like generators, this consumes the map iterator.
numbers_str = ['1', '2', '3', '4']
map_obj = map(int, numbers_str)
# Convert map object to list
map_as_list = list(map_obj)
print(f"Map as list: {map_as_list}") # Output: [1, 2, 3, 4]
# Access by index works on the list
first_num = map_as_list[0]
print(f"First number from list: {first_num}") # Output: 1
Output:
Map as list: [1, 2, 3, 4]
First number from list: 1
Alternatives: List Comprehension or for
loop
Often, a list comprehension is a more direct and readable alternative to using map
followed by list()
.
numbers_str = ['1', '2', '3', '4']
# Using list comprehension (often preferred over map)
numbers_list = [int(s) for s in numbers_str]
print(f"List from comprehension: {numbers_list}") # Output: [1, 2, 3, 4]
print(f"First number: {numbers_list[0]}") # Output: 1
# Using a for loop
map_obj = map(int, numbers_str)
print("\nIterating over map object:")
for num in map_obj:
print(num)
Output:
List from comprehension: [1, 2, 3, 4]
First number: 1
Iterating over map object:
1
2
3
4
Why the Error Occurs: The __getitem__
Method
Under the hood, using square brackets (object[key_or_index]
) calls the object's special __getitem__
method. Lists, tuples, strings, and dictionaries implement this method, making them subscriptable. Dictionary views, generators, map objects, and many other iterators do not implement __getitem__
, hence the TypeError
.
Conclusion
The TypeError: 'X' object is not subscriptable
(for dict_keys
, generator
, map
, etc.) arises when you try to use square bracket indexing ([]
) on objects that are iterators or views, not complete sequences stored in memory.
The universal solution is to convert the object to a list using list(your_object)
before attempting to access elements by index (list(your_object)[index]
).
Keep in mind:
- Converting generators or map objects to lists consumes them.
- Dictionary views (
.keys()
,.values()
,.items()
) reflect the dictionary at the time of conversion. - Consider if direct iteration (
for
loop) or alternatives like list comprehensions are more suitable than converting to a list just for indexing.