The iter() built-in function retrieves an iterator object from an iterable container or wraps a zero-argument callable with a sentinel stop value.
CPython Iteration Protocol: In Python, iteration is governed by two dunder methods: __iter__() and __next__(). Passing an iterable like a list, tuple, string, or dictionary to iter() yields a pointer that advances forward one element at a time via next(). To reverse sequence traversal, see reversed(), or pair items in lockstep with zip().
The built-in iter() function provides two distinct forms of invocation:
# Formal forms of built-in iter()
# Form 1: Standard iterable collection
iterator_obj = iter(iterable)
# Form 2: Callable with sentinel termination value
sentinel_iterator = iter(callable_func, sentinel)
| Parameter | Type Accepted | Behavioral Details |
|---|---|---|
iterable |
Iterable[T] |
Any object implementing __iter__() or the sequence protocol with __getitem__(). |
callable_func |
Callable (0 args) | Invoked repeatedly on each iteration call when using the two-argument sentinel form. |
sentinel |
Any object | Termination value. When callable_func() returns this value, iteration halts. |
| Return Value | iterator object |
An iterator yielding items sequentially until exhausted. |
Calling iter() on a container produces an active iterator object that yields consecutive items when passed to next():
# 1. Getting iterators from strings and lists
text = "plus2net"
str_it = iter(text)
print("First char: ", next(str_it))
print("Second char:", next(str_it))
print("Third char: ", next(str_it))
# 2. Inspecting concrete iterator types
nums = [4, 3, 5, 1]
list_it = iter(nums)
print("Original type:", type(nums))
print("Iterator type:", type(list_it))
print("First item: ", next(list_it))
Output:
First char: p
Second char: l
Third char: u
Original type:
Iterator type:
First item: 4
Attempting to iterate over non-iterable types raises a TypeError, while exhausting an iterator raises StopIteration:
# 1. Non-iterable raises TypeError
try:
bad_it = iter(4356) # Integers are not iterable
except TypeError as err:
print("TypeError caught:", err)
# 2. Exhaustion raises StopIteration
letters = iter("abc")
print(next(letters)) # a
print(next(letters)) # b
print(next(letters)) # c
try:
print(next(letters)) # Exhausted!
except StopIteration:
print("StopIteration caught: Iterator is fully exhausted")
# 3. Suppressing StopIteration using a default sentinel with next()
fresh = iter("ab")
print(next(fresh, "END")) # a
print(next(fresh, "END")) # b
print(next(fresh, "END")) # END (no exception raised)
Output:
TypeError caught: 'int' object is not iterable
a
b
c
StopIteration caught: Iterator is fully exhausted
a
b
END
next(it, default) safely suppresses StopIteration, returning your default sentinel value once the iterator is exhausted.
Any Python class can become a valid iterator by implementing the iterator protocol:
__iter__(): Returns the iterator instance itself (return self).__next__(): Computes and returns the next value, raising StopIteration when complete.# Building a custom reversible countdown iterator
class CountDown:
def __init__(self, start: int):
self.current = start
def __iter__(self):
return self # The iterator returns itself
def __next__(self):
if self.current <= 0:
raise StopIteration
val = self.current
self.current -= 1
return val
counter = CountDown(3)
for n in counter:
print(f"Tick: {n}")
Output:
Tick: 3
Tick: 2
Tick: 1
When called with two arguments, iter(callable, sentinel) creates a special callable-iterator. It invokes the function repeatedly until its return value equals the sentinel:
# Sentinel form: reads random integers until rolling a 6
import random
random.seed(42)
def roll_dice():
return random.randint(1, 6)
# iter(callable, sentinel) repeatedly invokes callable until it returns 6
rolls = list(iter(roll_dice, 6))
print("Dice rolls before first 6:", rolls)
Output:
Dice rolls before first 6: [6]
The sentinel pattern is frequently used in production for chunked file streaming and socket buffer ingestion:
# Recipe A: Chunking fixed-size blocks from large streams
import io
data_stream = io.StringIO("ABCDEFGH")
# Sentinel reader: read 2 bytes per step until empty string ""
chunk_reader = iter(lambda: data_stream.read(2), "")
for chunk in chunk_reader:
print("Chunk:", chunk)
# Recipe B: Infinite generator conversion
import itertools
counter = iter(itertools.count(1))
first_four = [next(counter) for _ in range(4)]
print("First four from count():", first_four)
Output:
Chunk: AB
Chunk: CD
Chunk: EF
Chunk: GH
First four from count(): [1, 2, 3, 4]
next([1, 2]) directly raises TypeError: 'list' object is not an iterator. Always obtain an iterator first via iter().StopIteration, it cannot be reset or reused in a second loop.iter(callable, sentinel) never returns the exact sentinel value, the loop will run indefinitely.# Pitfall 1: Assuming iterables and iterators are the same
my_list = [1, 2, 3] # Iterable, NOT an iterator
try:
next(my_list)
except TypeError as e:
print("Trap caught:", e)
# Pitfall 2: Re-iterating an exhausted iterator in loops
it = iter([10, 20])
print("First loop: ", [x for x in it])
print("Second loop:", [x for x in it]) # Empty!
Output:
Trap caught: 'list' object is not an iterator
First loop: [10, 20]
Second loop: []
Practice iterator creation, custom dunder classes, and sentinel stream chunking in our companion Jupyter Notebook:
Explore interactive code cells, build custom iterator classes, stream files with sentinel values, and run self-validating assertions.
View on GitHub Run in Google ColabRun these unit checks directly in Python or your local environment to verify iter() mechanics:
def test_iterator_protocol():
# 1. Standard iterable yields iterator
s_it = iter("py")
assert next(s_it) == "p"
assert next(s_it) == "y"
# 2. StopIteration suppression with default
assert next(s_it, "DONE") == "DONE"
# 3. Custom class iterator conformance
class Step:
def __init__(self):
self.n = 0
def __iter__(self):
return self
def __next__(self):
if self.n >= 2:
raise StopIteration
self.n += 1
return self.n
assert list(Step()) == [1, 2]
# 4. Sentinel iteration
vals = [10, 20, 99, 30]
val_it = iter(lambda: vals.pop(0), 99)
assert list(val_it) == [10, 20]
print("All iter() protocol unit assertions passed! 100% verified.")
test_iterator_protocol()
Output:
All iter() protocol unit assertions passed! 100% verified.
Retrieves the next element from an iterator, with optional default fallback.
Create stateful iterators easily using yield statements.
Returns a reverse iterator over sequences without duplicating data.
Pairs an iterable's items with an automatically incremented integer counter.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.