This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: How to make a class iterable using a member generator.
Type: Stage:
Components: Documentation Versions: Python 2.2
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: bpettersen, rhettinger, terry.reedy
Priority: normal Keywords:

Created on 2003-03-03 19:17 by bpettersen, last changed 2022-04-10 16:07 by admin. This issue is now closed.

Messages (3)
msg14922 - (view) Author: Bjorn Pettersen (bpettersen) Date: 2003-03-03 19:17
This should probably go at the end of 2.2.5 (LibRef). [I 
was trying all possible variations of __iter__ returning 
self, with next() as a method...]

"""If you are designing a class that should be iterable, 
i.e. you want to be able to say "for x in myClass:...", 
and, you also want the convenience of using a member 
function that is a generator to provide the items, your 
class' __iter__() method should return "self.myGenerator
(self)". The object returned from this call is an iterator-
generator that implements both of the required __iter__() 
and next() methods.

Example:
   class Range10:
       def __init__(self): self.scale = 5
       def __iter__(self):
           return Range10.generateItems(self)
       def generateItems(self):
           for i in range(10): yield i * self.scale

There are a couple of subtleties here. First, only "user-
defined functions" are converted to methods when 
accessed through a class or instance, i.e.myObject.foo
() will extract the foo function wrap it up as a method 
object, insert myObject in the argument list and call it 
(LibRef: 3.2/Callable types/User-defined methods). 
[thanks to Alex M. for clarifying this issue for me]

This automatic conversion does not happen for any 
other type of objects defined inside class scope. In our 
case, generateItems() will be a generator-iterator, i.e. 
not a "user-defined function". Thus the conversion to an 
instance method does not happen, and it is left as a 
static method of the class. [this seems like it might be 
a bug to me...].

To get around this irregularity, make sure your __iter__() 
calls generateItems() as a static method, and explictly 
passes self.
"""

-- bjorn
msg14923 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2003-03-09 17:57
Logged In: YES 
user_id=593130

As others have posted (with examples) on both c.l.py and 
py-dev, it is both possible and simpler to write __init__ 
itself as a generator function, thereby returning a 
generator-iterator object with the requisite __init_() and 
next() methods.. Since this does not seem to be obvious to 
many people, I would agree that *this* should be 
mentioned in the docs.  All the rest of the discussion about 
access and static methods is irrelevant for this purpose.

>>> from __future__ import generators
>>> class Range10:
...   def __init__(self,scale):
...     self.scale = scale
...   def __iter__(self):
...     scale = self.scale
...     for i in range(10): yield i * scale
...
>>> for i in Range10(4):
...   print i
...
0
4
8
12
16
20
24
28
32
36
msg14924 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2003-06-25 19:04
Logged In: YES 
user_id=80475

Expanded the docs as requested (though much more tersely 
worded).  See Doc/lib/libstdtypes.tex 1.128.
History
Date User Action Args
2022-04-10 16:07:18adminsetgithub: 38090
2003-03-03 19:17:48bpettersencreate