Question
How can I determine whether an object is iterable in Python? Is there a built-in function similar to isiterable()?
I have considered checking for an __iter__ attribute:
hasattr(my_obj, "__iter__")
However, I am unsure whether this check is reliable in every case.
Short Answer
By the end of this page, you will know what iteration means in Python, why checking for __iter__ alone is incomplete, and how to reliably test an object by calling iter() safely.
Concept
An iterable is an object that Python can loop over with for.
for item in value:
print(item)
Examples include lists, tuples, strings, dictionaries, sets, files, generators, and many custom objects.
Python determines whether an object can be iterated by attempting to create an iterator from it:
iterator = iter(value)
If this succeeds, value is iterable. If it raises TypeError, it is not iterable.
Many iterable objects implement __iter__(). However, Python also supports a legacy sequence mechanism: an object can be iterable when it provides indexed access through __getitem__() starting at index 0, even if it has no __iter__ method. Therefore, hasattr(value, "__iter__") is not a complete test.
The most reliable general-purpose answer is to use Python's actual iteration protocol: call iter() and handle TypeError.
Mental Model
Think of an iterable as a book that Python knows how to read one item at a time.
iter(book)asks Python to open the book at its first readable item.- The returned iterator is the bookmark that remembers the current position.
next(iterator)reads the next item and moves the bookmark.- When there are no items left, Python raises
StopIteration.
Checking only for __iter__ is like checking whether a book has a particular kind of cover. Usually that works, but Python can also read some objects through another supported mechanism: numbered pages supplied by __getitem__. Trying iter() checks whether Python can actually open and read the object.
Syntax and Examples
Use iter() inside try/except when you need a reliable behavioral check.
def is_iterable(value):
try:
iter(value)
return True
except TypeError:
return False
print(is_iterable([1, 2, 3])) # True
print(is_iterable("hello")) # True
print(is_iterable(42)) # False
print(is_iterable(None)) # False
iter(value) follows the same rules that a for loop uses. A list and string produce iterators, while an integer and None do not.
If you are checking whether an object explicitly follows the modern iterable interface, use collections.abc.Iterable:
Step by Step Execution
Consider this function call:
def is_iterable(value):
try:
iter(value)
return True
except TypeError:
return False
result = is_iterable("cat")
Step by step:
is_iterablereceives the string"cat"asvalue.iter(value)asks Python for an iterator over the string.- Python successfully creates a string iterator.
- No exception occurs, so the function returns
True. resultbecomesTrue.
Now with an integer:
result = is_iterable(10)
iter(10)is attempted.- Integers do not provide a valid iteration mechanism.
- Python raises
TypeError.
Real World Use Cases
Testing iterability is useful when a function accepts either one value or a collection of values.
Accept one tag or several tags
def normalize_tags(tags):
if isinstance(tags, str):
return [tags]
try:
return list(tags)
except TypeError:
return [tags]
print(normalize_tags("python")) # ['python']
print(normalize_tags(["python", "testing"])) # ['python', 'testing']
Strings need special handling because they are iterable character by character, but an API may intend a string to be one value.
Validate data before processing
A data-processing function may require a sequence of records:
def process_records(records):
try:
iterator = iter(records)
except TypeError:
raise TypeError("records must be an iterable") from None
record iterator:
(record)
Real Codebase Usage
In real projects, developers often avoid a separate is_iterable() check when they are about to iterate anyway. Instead, they attempt the required operation and provide a useful error if it fails.
Validate at a function boundary
def total(numbers):
try:
return sum(numbers)
except TypeError as error:
raise TypeError("numbers must contain numeric iterable values") from error
Use a guard clause for special iterable types
Strings, bytes, and dictionaries are iterable, but their iteration behavior may not match an application's meaning.
from collections.abc import Iterable
def display_items(value):
if isinstance(value, (str, bytes)):
return [value]
if not isinstance(value, Iterable):
return [value]
return list(value)
Preserve one-pass iterators
Common Mistakes
Checking only for __iter__
hasattr(my_obj, "__iter__")
This can miss objects that are iterable through __getitem__() rather than __iter__().
class LegacySequence:
def __getitem__(self, index):
if index >= 3:
raise IndexError
return index * 10
value = LegacySequence()
print(hasattr(value, "__iter__")) # False
print(list(value)) # [0, 10, 20]
Prefer iter(value) in a try/except TypeError block for a runtime check.
Assuming an iterable is reusable
Generators are iterable, but they are usually consumed after one pass.
Comparisons
| Approach | What it checks | Best use | Limitation |
|---|---|---|---|
iter(value) with TypeError handling | Whether Python can actually obtain an iterator | General runtime validation | Requires try/except |
isinstance(value, Iterable) | Whether the object matches the Iterable abstract base class | Interface-oriented checks and type validation | May not recognize legacy __getitem__ iteration |
hasattr(value, "__iter__") | Whether an attribute named __iter__ exists | Rarely appropriate as a quick inspection | Does not prove iteration works and misses legacy sequences |
Cheat Sheet
# Reliable runtime test
def is_iterable(value):
try:
iter(value)
return True
except TypeError:
return False
# Interface-oriented check
from collections.abc import Iterable
isinstance(value, Iterable)
iter(value)is the same protocol aforloop relies on.- Catch
TypeError, not every exception. - Do not rely solely on
hasattr(value, "__iter__"). - Strings, bytes, and dictionaries are iterable; decide whether that is desirable for your API.
iter(value)does not consume items.next(iter(value))can consume an item whenvalueis already an iterator.- Iterators are iterable, but some are one-pass objects such as generators.
FAQ
Is there a built-in isiterable() function in Python?
No. The usual runtime test is iter(value) inside try/except TypeError.
Is hasattr(obj, "__iter__") a reliable iterable check?
No. It can miss objects that Python iterates using __getitem__(), and the presence of an attribute does not guarantee a successful iteration operation.
What is the recommended way to check if an object is iterable?
Use iter(obj) and catch TypeError when you need to know whether a for loop could iterate over it.
Are strings iterable in Python?
Yes. Iterating over a string yields one-character strings. Handle strings separately if your code should treat one string as a single value.
Is a dictionary iterable?
Yes. Iterating over a dictionary yields its keys. Use .values() for values or .items() for key-value pairs.
What is the difference between an iterable and an iterator?
An iterable can create an iterator. An iterator additionally tracks progress and provides items through next().
Mini Project
Description
Build a small input-normalization utility for an application setting that accepts either one label or many labels. The utility demonstrates how to detect iterability while treating strings as single labels rather than character collections.
Goal
Create a function that converts supported input into a clean list of non-empty label strings.
Requirements
- Accept a single string as one label.
- Accept an iterable of labels, including a generator.
- Reject non-iterable input with a clear
TypeError. - Reject labels that are not strings.
- Remove empty labels after whitespace is stripped.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Add Rows to a Pandas DataFrame in Python
Learn how to add rows to a Pandas DataFrame, why repeated row appends are slow, and when to use loc, concat, or record lists.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.