Question

How to Check Whether an Object Is Iterable in Python

pythoniterable

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.

Take Quiz

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:

from collections.abc import Iterable

print(isinstance([1, 2, 3], Iterable))  # True
print(isinstance(42, Iterable))         # False

This is often useful for type-oriented code, but iter() is the broader runtime test because it also recognizes legacy __getitem__-based iterables.

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:

  1. is_iterable receives the string "cat" as value.
  2. iter(value) asks Python for an iterator over the string.
  3. Python successfully creates a string iterator.
  4. No exception occurs, so the function returns True.
  5. result becomes True.

Now with an integer:

result = is_iterable(10)
  1. iter(10) is attempted.
  2. Integers do not provide a valid iteration mechanism.
  3. Python raises TypeError.
  4. The except TypeError block runs.
  5. The function returns False.

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

    for record in iterator:
        print(record)

Work with generators and files

Generators and open file objects are iterable, so functions can process them without needing all data in memory at once.

Take Quiz

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

Do not call list() just to test an iterable if it may be a large or one-use generator. iter(value) only requests an iterator; it does not consume items.

def print_first_item(values):
    iterator = iter(values)
    print(next(iterator))

Calling next() does consume one item, so only do that when consuming an item is intended.

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.

values = (n * 2 for n in range(3))
print(list(values))  # [0, 2, 4]
print(list(values))  # []

If the data must be reused, store it in a list when appropriate.

Treating strings as collections of application values

list("admin")  # ['a', 'd', 'm', 'i', 'n']

A string is iterable, but often represents one username, tag, or message. Handle str separately when your function expects a collection of whole values.

Catching every exception

try:
    iter(value)
except Exception:
    return False

Do not catch Exception for this purpose. A custom object's iteration code could raise another error that indicates a real bug. Catch TypeError, which is the expected signal for a non-iterable value.

Confusing an iterable with an iterator

Every iterator is iterable, but not every iterable is itself an iterator. Lists can create iterators; list iterators remember progress.

Comparisons

ApproachWhat it checksBest useLimitation
iter(value) with TypeError handlingWhether Python can actually obtain an iteratorGeneral runtime validationRequires try/except
isinstance(value, Iterable)Whether the object matches the Iterable abstract base classInterface-oriented checks and type validationMay not recognize legacy __getitem__ iteration
hasattr(value, "__iter__")Whether an attribute named __iter__ existsRarely appropriate as a quick inspectionDoes not prove iteration works and misses legacy sequences
for item in valuePerforms iteration directlyWhen you simply need to process valuesRaises TypeError if value is not iterable

An iterable can produce an iterator:

numbers = [1, 2, 3]       # iterable
iterator = iter(numbers)  # iterator

An iterator also supports next() and keeps its current position:

print(next(iterator))  # 1
print(next(iterator))  # 2
Take Quiz

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 a for loop 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 when value is 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().

Does calling iter() consume a generator?

No. Calling iter(generator) returns the same generator object without advancing it. Calling next() or looping over it consumes values.

Take Quiz

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.
Take Quiz

Keep learning

Related questions

View more