Python Tutorials - Herong's Tutorial Examples - v2.15, by Herong Yang
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
Variables, Operations and Expressions
Function Statement and Function Call
Iterable Objects of Built-in Data Types
What Is Filtered Generator Expression
What Is Double-Generator Expression
List, Set and Dictionary Comprehensions
Packages and Package Directories
"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