What Is Iterable Object

This section provides a quick introduction on iterable object, which is an object with a required instance method to return an iterator object.

What Is Iterable Object - An iterable object is an object that supports a required instance method, iterable.__iter__(), to return an iterator object.

Once you have an iterable object created, you can call the built-in method iter(iterable) to retrieve an iterator object. iter(iterable) will call iterable.__iter__() for you. So the following statements are equivalent:

iter(iterable)        # preferred way to retrieve an iterator object
iterable.__iter__()

The main purpose of using an iterable object is to retrieve an iterator object and iterate over its items in a "for" loop statement.

for item in iterable:
   ...

The above "for" statement is equivalent to:

iter = iter(iterable)
try:
   while True:
      item = next(iter)
      ...
except StopIteration:
   pass

To create an iterable object, you can create two classes: one iterator class with the required __next__() method, and one iterable class with the required __iter__() method. Then you call the iterable class constructor to create an iterable object as shown in the example code below:

#  iterable_test.py
#- Copyright 2011 (c) HerongYang.com. All Rights Reserved.
#

# define an iterator class
class top3:
   items = ['C', 'Java', 'JavaScript']

   def __init__(self):
      self.i = 0

   # define the required method by the iterator interface for Python 3
   def __next__(self):
      if self.i < 3:
         self.i += 1
         return top3.items[self.i-1]
      else:
         raise StopIteration()

   # define the required method by the iterator interface for Python 2
   def next(self):
      return self.__next__()

# define an iterable class
class factory:

   # define the required method by the iterable interface
   def __iter__(self):
      return top3()

# create an iterable object and use it in "for" statement
f = factory()
for i in f:
   print(i)

If you run this sample code, you should get:

herong> python iterable_test.py

C
Java
JavaScript

Since the iterable object and the iterator object are closely related, you can actually create a single class to produce a single object who carries both the iterable and iterator interfaces. Here is an example code:

#  iterable_modified.py
#- Copyright 2011 (c) HerongYang.com. All Rights Reserved.
#

# define an iterator+iterable class
class top3:
   items = ['C', 'Java', 'JavaScript']

   def __init__(self):
      self.i = 0

   # define the required method by the iterable interface
   def __iter__(self):
      return self

   # define the required method by the iterator interface for Python 3
   def __next__(self):
      if self.i < 3:
         self.i += 1
         return top3.items[self.i-1]
      else:
         raise StopIteration()

   # define the required method by the iterator interface for Python 2
   def next(self):
      return self.__next__()

# create an iterable object and use it in "for" statement
f = top3()
for i in f:
   print(i)

If you run this sample code, you should get:

herong> python iterable_modified.py

C
Java
JavaScript

The iterable object created from the modified class "top3" works perfectly in a single "for" loop. But if you use it again in another "for" loop, no items come out from the iterator as shown in the following code:

herong$ more iterable_issue.py
#  iterable_issue.py
#- Copyright 2011 (c) HerongYang.com. All Rights Reserved.
#

# define an iterator+iterable class
class top3:
   items = ['C', 'Java', 'JavaScript']

   def __init__(self):
      self.i = 0

   # define the required method by the iterable interface
   def __iter__(self):
      return self

   # define the required method by the iterator interface for Python 3
   def __next__(self):
      if self.i < 3:
         self.i += 1
         return top3.items[self.i-1]
      else:
         raise StopIteration()

   # define the required method by the iterator interface for Python 2
   def next(self):
      return self.__next__()

print('create an iterable object and use it in "for" statement:')
f = top3()
for i in f:
   print(i)

print('use the same iterable in "for" statement again:')
for i in f:
   print(i)


herong$ python iterable_issue.py

create an iterator object and use it in "for" statement:
C
Java
JavaScript

use the same iterable in "for" statement again:

Why? The root cause is in the __iter__() method. When it called, we should return an iterator with the internal position pointer reset to 0. Here is the corrected version of the code.

herong$ more iterable_corrected.py
#  iterable_corrected.py
#- Copyright 2011 (c) HerongYang.com. All Rights Reserved.
#

# define an iterator+iterable class
class top3:
   items = ['C', 'Java', 'JavaScript']

   def __init__(self):
      self.i = 0

   # define the required method by the iterable interface
   def __iter__(self):
      self.__init__()    # reset the object before returning it
      return self

   # define the required method by the iterator interface for Python 3
   def __next__(self):
      if self.i < 3:
         self.i += 1
         return top3.items[self.i-1]
      else:
         raise StopIteration()

   # define the required method by the iterator interface for Python 2
   def next(self):
      return self.__next__()

print('create an iterable object and use it in "for" statement:')
f = top3()
for i in f:
   print(i)

print('use the same iterable in "for" statement again:')
for i in f:
   print(i)

print('create an iterable object and retrieve its iterator:')
x = top3()
y = iter(x)
print("iterable object identity =", id(x))
print("iterator object identity =", id(y))


herong$ python iterable_corrected.py

create an iterable object and use it in "for" statement:
C
Java
JavaScript

use the same iterable in "for" statement again:
C
Java
JavaScript

create an iterable object and retrieve its iterator:
('iterable object identity =', 4458757656)
('iterator object identity =', 4458757656)

As you can see from the last part of the above output, we have a single object which is an "iterable" and an "iterator" at the same time. This is fine in a single-thread process.

But in a multi-thread process, the same "top3" object could be used in 2 "for" statements in 2 threads at the same time. Then both "for" statements will not perform correctly, since they are sharing the same iterator object.

So in the finalized version of the code shown below, I returns a new "top3" object, each time when __iter__() is called.

herong$ more iterable_finalized.py
#  iterable_finalized.py
#- Copyright 2011 (c) HerongYang.com. All Rights Reserved.
#

# define an iterator+iterable class
class top3:
   items = ['C', 'Java', 'JavaScript']

   def __init__(self):
      self.i = 0

   # define the required method by the iterable interface
   def __iter__(self):
      return top3()      # return a brand-new object

   # define the required method by the iterator interface for Python 3
   def __next__(self):
      if self.i < 3:
         self.i += 1
         return top3.items[self.i-1]
      else:
         raise StopIteration()

   # define the required method by the iterator interface for Python 2
   def next(self):
      return self.__next__()

print('create an iterable object and use it in "for" statement:')
f = top3()
for i in f:
   print(i)

print('use the same iterable in "for" statement again:')
for i in f:
   print(i)

print('create an iterable object and retrieve its iterator:')
x = top3()
y = iter(x)
print("iterable object identity and type =", id(x), type(x))
print("iterator object identity and type =", id(y), type(y))


herong$ python iterable_finalized.py

create an iterable object and use it in "for" statement:
C
Java
JavaScript

use the same iterable in "for" statement again:
C
Java
JavaScript

create an iterable object and retrieve its iterator:
iterable object identity and type = 4400231184 <class '__main__.top3'>
iterator object identity and type = 4400231760 <class '__main__.top3'>

Table of Contents

 About This Book

 Running Python Code Online

 Python on macOS Computers

 Python on Linux Computers

 Built-in Data Types

 Variables, Operations and Expressions

 Statements - Execution Units

 Function Statement and Function Call

Iterators and Generators

 What Is Iterator Object

What Is Iterable Object

 Iterable Objects of Built-in Data Types

 What Is Generator Iterator

 What Is Generator Expression

 What Is Filtered Generator Expression

 What Is Double-Generator Expression

 List, Set and Dictionary Comprehensions

 Classes and Instances

 Modules and Module Files

 Packages and Package Directories

 "sys" and "os" Modules

 "pathlib" - Object-Oriented Filesystem Paths

 "pip" - Package Installer for Python

 SciPy.org - Python Libraries for Science

 pandas - Data Analysis and Manipulation

 Anaconda - Python Environment Manager

 Jupyter Notebook and JupyterLab

 References

 Full Version in PDF/EPUB