How to make iterators out of Python functions without using yield

The built-in iter() method in Python 2.2 and later hides a neat trick which is not well-known. Typically iter() is used to return an iterator from an iterable object:

i = iter(iterable_object)
next(i)

However, iter() can also be used in an entirely different way. If you have a function that you need to call multiple times until a particular value is returned, then iter() will let you wrap that function into a convenient iterator. To do this, pass the function as the first argument, and the return value to end with as the second argument:

iter(repeated_function, stop_value)

For example, you could read a text file until you reached a certain text marker:

with open('file.txt') as f:
    for line in iter(f.readline, '#~END_OF_IMPORTANT_TEXT'):
        do_something(line)

One restriction is that the function passed to iter() can't take any arguments. You can use either a lambda expression, or functools.partial() to "fill in" a function's arguments before using it with iter(). For example, here are two ways you could read 4KB at a time from a source object that has a file-like read() method:

# Using a lambda expression
for chunk in iter(lambda: some_source.read(4096), ''):
    do_something(chunk)

# Using functools.partial()
from functools import partial
read_chunk = partial(some_source.read, 4096)
for chunk in iter(read_chunk, ''):
    do_something(chunk)

Posted 11/2/2013